home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
PROGRAM
/
FLEXFILE.ARJ
/
FLEXINFO.EXE
/
FLEXFILE.DOC
< prev
next >
Wrap
Text File
|
1991-10-22
|
195KB
|
5,663 lines
FlexFile■
(C) Copyright 1990 Ganahl Software
All Rights Reserved
This document is supplied as an introduction to FlexFile. Although it
contains almost all of the FlexFile documentation, it is not part of the
FlexFile commercial package. Please feel free to distribute this file
freely to any interested party.
This does NOT imply that files in the .OBJ format or .LIB format from
the FlexFile commercial package may be distributed in any manner.
GANAHL Software
P.O. Box 4275
Deerfield Beach, Fl 33442
Tel: (305) 566-1796
Fax: (305) 566-3517
Overview
Variable Length Fields
FlexFile is a very focused dual purpose tool kit for Clipper
programmers. Its first and main purpose is to provide a
complete replacement for Clipper's memo-fields with a more
powerful and more efficient Variable Length Field engine. We
are certain that it will relieve you of the confinement
imposed by Clipper's memo-fields, but also, we believe it may
change the way you think about your data base structures in
general.
FlexFile was designed to be immediately familiar to the
Clipper programmer. To accomplish this goal, many of the
function names can only be distinguished from their Clipper
counterpart by a V_ prefix. For example, V_USE() opens a DBV
file in the currently V_SELECT()ed work area.
The similarities extend into the structural use of FlexFile's
Variable Length Fields (VLF) as well. FlexFile's version of a
memo-field (which we will refer to as a pointer-field) is a
six byte character type field in a DBF file. FlexFile uses
this pointer-field to store a pointer to variable length data
in a .DBV file (FlexFile's version of a .DBT file). These
similarities allow any Clipper REPLACE syntax which works on
memo-fields to remain almost unchanged when used with
FlexFile. This claim carries in network environments as well.
The benefits and use of FlexFile's Variable Length Fields are
discussed in detail in the Reference "Variable Length Fields".
Pay special attention to the section on FlexFile's extended
features.
Arrays
FlexFile's strongly typed arrays provide a utility which defy
the products name. Instead of being flexible, these arrays
are purposefully rigid. The utility of these arrays is
twofold. First and foremost, they use far less space than
their Clipper counterpart. For example, any Clipper array of
1,000 elements would require a minimum of 14,000 bytes from
Clipper's free pool of memory. A FlexFile array of 1,000
integers, on the other hand, would require only 2,000 bytes
from Clipper's free pool of memory.
Typically, large strongly typed arrays are used for scientific
or statistical matrixes. For this reason, FlexFile provides a
host of functions which globally manipulate or query these
large arrays.
Linking FlexFile into an application
FlexFile is compatible with all the linkers that it has been
tested with. The entire library can be overlayed with
Blinker(tm) or WarpLink(tm) or RTLink Plus(tm).
The procedure for linking is the same as for any other library
listed in your link file or on the link command line. For
example:
link test.obj,,,c:\lib\clipper c:\lib\flexfile
Each FlexFile function begins with either a V_ or an A_
depicting use with the Variable Length Field manager or the
array manager, respectively. The prefix to each function was
chosen to avoid conflict with other libraries. If you find a
conflict with another library, you must place FlexFile before
the other library in the link list in order to use the
FlexFile function. If you find a conflict please contact us
so that we may avoid them in future releases.
Variable Length Fields
The enhancements to memo-fields
FlexFile addresses the four major problems that plague
Clipper's implementation of memo-fields.
First, FlexFile reuses all unused space. If your user edits a
memo (or deletes elements of an array) making it smaller,
FlexFile is aware of every byte of the excess space and will
use it later for new data. This feature totally eliminates
the file bloat that characterizes Clipper's DBT files.
(Clipper frequently abandons space no longer needed by
memo-field data, and once abandoned, not even packing the file
can regain the space.)
Second, FlexFile does not allocate space in blocks. Instead,
each item stored requires only the space for its own length
plus a six byte header that FlexFile uses for management.
This is in great contrast to Clipper's memo-fields that
require a fixed block size of 512 bytes. This feature alone
will save disk space at an average of 256 bytes per record.
Third, the size of FlexFile's Variable Length Field (VLF)
files are limited only by your disk space. This is in
contrast to Clipper's size limit on a DBT file of 16M.
Finally, FlexFile can store any valid Clipper data. This
includes arrays, character strings, dates, numerics and
logicals. In addition, the content of character data is not
limited as it is in Clipper. It is, therefore, possible to
store SAVESCREEN() variables or any other binary data that you
use.
FlexFile's Extended Features
Through FlexFile's ability to save an array to a field, your
DBF's can become three dimensional (actually multi-dimensional
with Clipper 5.0). Looking at it another way, each record can
now have a variable number of fields. This new feature allows
far greater flexibility in designing a database system.
Lets take the simple example of storing a telephone number.
In a customer data base, next to the name and address we store
the telephone number. But is one telephone number enough?
Most businesses require at least a voice line and a fax line.
Then again, there is that one guy who has got two voice lines,
a fax line, a data line, and a whole bunch more at his house.
So, do we build in five or ten telephone fields in the
customer file? Certainly this situation would not warrant a
second file and an index with a relation. Usually, we tell our
client (very nicely) to live with a maximum of two.
With FlexFile, however, the answer is simple. Save an array
of telephone numbers. Those customers who have one or two
telephone numbers use only the space for one or two telephone
numbers. Those that have ten can have ten. There are no
indexes or relations to maintain and you can extend this
concept into those perplexing data item which your client
defines as requiring "usually two or three, but sometimes
twenty or more".
FlexFile also allows the programmer to store any valid Clipper
data type to a field. At first look, this alternative seems
less than impressive. Why would you want to store a logical
value, which takes only one byte per record in a DBF file, by
using a six byte pointer-field in the DBF to point to an
object which requires a six byte header in the DBV. The
advantage does not come from saving any one type, but rather,
from being able to store different types to the same field.
This is particularly advantageous in data driven applications.
For example, if you give your clients user defined fields,
they can change the type of a field and you won't have to
worry about carrying extra fields for each type "Just in
Case".
USEing DBV files
The use of FlexFile's DBV files was designed to parallel the
use of DBF type files. Specifically, the system is made up of
work areas that are used to manage the VLF files in exactly
the same manner as Clipper uses work areas to manage DBF
files. This method avoids the need to keep track of
"handles", and allows the use of aliases.
For example, you can V_SELECT(1) and then V_USE() a file in
area one, and then V_SELECT(0) the next available work area
and V_USE() another file there. It should be noted that
FlexFile's work areas are mutually exclusive from Clippers DBF
work areas. For example, you can V_SELECT(1) and V_USE() a
file in VLF area one, and simultaneously, SELECT 1 and USE a
file in DBF area one with no conflicts.
The FlexFile system offers an unlimited number of work areas,
however, you must decide what your maximum number of work
areas will be before you open the first DBV type file. The
reason for this is to allow the system to create one
contiguous block of memory for the management of the files
instead of fragmenting memory with little blocks each time you
open a new file. At any point that you have all DBV files
closed you can readjust this maximum number of work areas by
using the V_FILES() function.
Creating DBV files
The simplest thing to do in FlexFile is to create a DBV type
file. Simply V_USE() a file that does not exist and you will
have a DBV file open and ready to go.
How to Manage Your Data with FlexFile
Another great use for FlexFile is to hold data that is not
repetitive and structured; the DBF file structure is not at
all accommodating in this area. For example, almost every
Clipper application requires system data such as a color
scheme, default directories, default drive for temporary
files, printer definitions, and other randomly sized
non-repetitive data. Storing this data is now a trivial
matter.
In this case, the design could be as simple as a two field DBF
file with an index, and one FlexFile VLF file. The DBF
structure might look like:
Structure for database: C:\FF\SYSTEM.DBF
Number of data records: 20
Date of last update : 10/05/90
Field Field Name Type Width Dec
1 VAR_NAME Character 10
2 VLF_FLD Character 6
** Total ** 17
The key field <var_name> will hold the name of a Clipper
variable that you use; the six byte character field <vlf_fld>
FlexFile will use to hold a pointer to <var_name>'s value.
Following from the above description, start-up code for an
application might look like:
// Declare the system wide public variables that you use.
PUBLIC aColors,; // Array variable for colors
cDefaultDir,; // A user defined default directory
dYearEnd // A user defined year end date.
// Open the DBF, its index, and the VLF
USE System.dbf INDEX System.ntx
V_USE( "System.dbv" )
// Seek your colors array and retrieve it.
// Note that this Clipper 5.0 syntax for arrays is slightly
// different than the S'87 syntax. See the V_FILLARR() function.
SEEK "aColors"
aColors = V_RETRIEVE( vlf_fld )
// Seek the record that holds a "Default" directory.
SEEK "cDefaultDir"
cDefaultDir = V_RETRIEVE( vlf_fld )
// Dates can be stored as easily as anything else.
SEEK "dYearEnd"
dYearEnd = V_RETRIEVE( vlf_fld )
Of course, for this code to work you must have previously save
data to these records. The next section on FlexFile's
pointers gives an in depth view into storing the data,
retrieving it and replacing old data with new.
FlexFile's Pointers
The key to maintaining secure data with FlexFile is fully
understanding the pointers that FlexFile uses to access data.
Clipper's memo-fields maintains a similar set of pointers but
Clipper hides them from the programmer as well as the user.
If you try to assign a Clipper memo-field pointer directly to
a variable as in:
var = system->mem_fld // Clipper memo-field
Clipper intercepts the call and automatically goes to the DBT
file and fetches the data that <mem_fld> points to. With
FlexFile, this assignment would put the pointer into <var>
instead of the data. So, the equivalent code using flexfile
is:
var = V_RETRIEVE( system->vlf_fld ) // FlexFile VLF
What we are doing here is passing the V_RETRIEVE() function a
pointer to the data and V_RETRIEVE() is kindly passing us back
data that was stored there.
The same principal applies to saving data... with a twist.
When Clipper saves data to a memo-field, it allocates disk
space in 512 byte chunks and sticks a pointer to the allocated
space in the memo-field of the DBF file. For example,
// Clipper hogs 512 bytes with this one.
REPLACE system->mem_fld WITH "This will take 512 bytes"
Clipper allocates 512 bytes of disk space, puts the 24 bytes
of data in the front of the new space, and puts a pointer to
the block in <mem_fld>. If, without moving the record
pointer, we say
// Clipper abandons the first mess and requires 1024 more.
REPLACE system->mem_fld WITH SPACE( 513 )
then Clipper will abandon the first 512 byte block
(permanently), and allocate 1024 bytes for the new data. The
old pointer to the 512 byte block is lost and the file space
is gone forever.
FlexFile is smarter than this. Using FlexFile, we would save
the 24 byte string with:
// FlexFile neatly allocates 30 bytes of disk space.
REPLACE system->vlf_fld WITH ;
V_REPLACE( "This will take 30 bytes", system->vlf_fld )
If we follow the pointer in this example, we can see what
FlexFile is going to do. The first thing that we notice is
that we not only pass to the V_REPLACE() function the new
data, but also, we give it the old pointer.
Because this function will operate from right to left, first
it will look at the right most <vlf_fld> to see if it is
pointing to any old data. If it is not, then the function
proceeds to look through an internal table for other vacated
space. We will assume for this example that this is the first
V_REPLACE(). Therefore, there is no available space that has
already been release by other data, so the new string is
appended to the end of the file.
To do this FlexFile will allocate 30 bytes of disk space, 24
bytes for the data and six bytes to keep some information that
it uses for management.
Now we continue the example by replacing the thirty bytes with
513 bytes of spaces:
// FlexFile releases 30 bytes and stores 519.
REPLACE system->vlf_fld WITH ;
V_REPLACE( SPACE( 513 ), system->vlf_fld )
This time, FlexFile sees that the old pointer has something in
it, and so releases it. Then it seeks internally for an
available 519 byte space to put the new information. There is
none, so it tags this at the end of the file as well.
There are two very important advantages you can see from this
example. First, only the size of the data was required to
keep the data, and second, the next piece of data that you
store that is thirty bytes or less will be stored in the
vacated space.
The thirty byte space that is available brings up an important
point. We have run tests where one VLF file undergoes
millions of V_REPLACE()s. The data ranged in random sizes
from one byte to ten thousand bytes. The amount of vacant
space tended to level off at about 20% of the fully pack
file's size and at that point the VLF file stops growing,
period.
Disaster Recovery
One of FlexFile's advantages is that it is very robust against common
disaster situations such as power loss. Your DBV file may get damaged
during such a calamity, however, the damage done is local to the write
that was in progress when the interuption occurred. That is, there is
no immediate ripple effect from a damaged section of a DBV to non
damaged sections.
On the other hand, if your file gets damaged and you continue to use it,
you CAN cause more damage. Therefore, you should prepare your code for
such an occurance (you can always rely on backups, but sometimes clients
simply fail to do their job of backing up regularly).
If V_REPLACE() or V_DELETE() return a logical value (as opposed to a
character value) and/or V_ERROR() reports a 6200, 6500, 6700 or 9000
class of error, the DBV file may be corrupt. (Syntax errors in calling
FlexFile functions can also cause these errors so check your syntax
first.)
When you are certain that your syntax is not causing the problem, you
should rebuild the DBV file. Rebuilding is a simple process and is
outlined by the following code. The code is based on a DBF file called
"dbf_file.dbf" which has a six byte character field (a FlexFile
pointer-field) called <vlf>. We then open the existing DBV and create a
new DBV into which to copy the old data. Finally, we erase the old DBV
and rename the new one back to the old.
Before any code such as this is implemented you should back up the files
involved.
// Open the controlling DBF and the DBV (with an alias of "OLD")
USE dbf_file
V_USE( "dbv_file", "old" )
// Open a new DBV to recieve the copyable data (with an alias of "NEW").
V_SELECT(0)
V_USE( "tempdbv", "new" )
DO WHILE !EOF()
IF V_TYPE( dbf_file->vlf, "old" ) == 'U'
QOUT( "This record is damaged." )
WAIT
LOOP
ENDIF
// If the type is Char, Num, Log, Date, array or FlexFile array
IF V_TYPE( dbf_file->vlf, "old" ) $ "CNLDAF"
// Get the value from the DBV.
var = V_RETRIEVE( dbf_file->vlf, "old" )
// Write the value to the new DBV.
dbf_file->vlf := V_REPLACE( var, space(6), "new" )
ENDIF
SKIP
ENDDO
FERASE( "dbv_file.dbv" )
FRENAME( "tempdbv.dbv", "dbv_file.dbv" )
The other form of "disaster" that is fairly common is to loose a DBF
file that has pointer-fields into a DBV. This is a very serious
situation because there is nothing in the DBV which points back into the
DBF. However, there is a new function V_WALK_DBV() that allows the
programmer to step from field to field through the DBV file. The steps
go from physical field to physical field and so the logical order of the
file is lost. However, in extreme situations this function may be
helpful. See V_WALK_DBV() for more information.
Saving and Retrieving Clipper's arrays
Saving and retrieving Clipper S'87 arrays
To save a an array using the S'87 version of the library
(Flex_S87.lib), you treat the array like any other variable.
For example,
&& Declare an array
DECLARE arr[100]
&& Put some junk in it.
afill( arr, "Solid waste" )
&& Save the array (assume a DBF and DBV are open).
REPLACE vlf WITH V_REPLACE( arr, vlf )
To retrieve that same array in S'87 takes more than one line of code.
Remember that V_LEN() returns the total number of elements in
a S'87 array.
&& Assume that we are still on the same record in
&& the DBF but we do not already have a declared array.
&& First we need to DECLARE the array to be the size of
&& the array we stored above.
DECLARE newarr[ V_LEN(vlf) ]
&& Now we fill the array.
V_FILLARR( newarr, vlf )
Although that was slightly awkward that is all there is to it.
Saving and retrieving Clipper 5.0 arrays
To save an array with the Clipper 5.0 library (FlexFile.lib)
you treat the array like you would any other Clipper variable.
// Save the original array
REPLACE vlf WITH V_REPLACE( arr, vlf )
// Retrieve it later
newarr = V_RETRIEVE( vlf )
Indexing on Variable Length Fields
There are several issues that must be addressed by the programmer in
order to create and maintain an index on part (or all) of a FlexFile
Variable Length Field.
First, creating an index is as easy as:
INDEX ON V_RET( vlf_fld, "ALIAS_1", 1, 15 ) TO NTX_FILE.NTX
This would create an index on the first fifteen characters of a FlexFile
VLF. However, every element of a clipper index must be the same length
and the above example does not guarantee this. (If V_RETRIEVE() returns
a string of less than 15 characters, you will corrupt your index.) So,
you should PAD() the return from V_RETRIEVE() with spaces as in:
INDEX ON PAD( V_RET(.....), 15 ) TO NTX_FILE.NTX
The second issue is to note that only data of the same type can be
indexed in this manner. Accordingly, make sure that the character data
is not binary data. If a CHR(0) is found in the first 15 characters of
the above example, Clipper will consider that to indicate the end of the
string. This will make the string's length different from other strings
in the index and, thereby, corrupt the index.
The last issue is described below, but is more complex. However, it is
not necessary to understand why, as long as you always abide by the
rule: You must always have a REPLACE command that does not have a
V_REPLACE in its syntax before REPLACEs that contain V_REPLACE(). For
example,
&& INCORRECT....will corrupt the index if the index relys on either
&& the data pointed to by vlf_1 or vlf_2.
REPLACE vlf_1 WITH V_REPLACE( "testing", vlf_1 )
REPLACE vlf_2 WITH V_REPLACE( "testing", vlf_2 )
SKIP
&& CORRECT....the first replace is a "dummy" but maintains integrity.
REPLACE any_fld WITH any_fld
REPLACE vlf_1 WITH V_REPLACE( "testing", vlf_1 )
REPLACE vlf_2 WITH V_REPLACE( "testing", vlf_2 )
SKIP
First, the only reason I placed two V_REPLACE()s in the example is to
show that only one "dummy" replace is necessary before any number of
imbedded V_REPLACE()s. Remember that anything that causes the index to
be updated (such as COMMIT, SKIP, SEEK, etc.) require that a new "dummy"
replace be used before attempting another REPLACE with imbedded
V_REPLACE()s. For example,
&& CORRECT
REPLACE any_fld WITH any_fld
REPLACE vlf_1 WITH V_REPLACE( "testing", vlf_1 )
REPLACE vlf_2 WITH V_REPLACE( "testing", vlf_2 )
SKIP
&& INCORRECT....will corrupt the index without another "dummy"
&& replace after the COMMIT. Assuming, of course, that vlf_2
&& is included in the index key.
REPLACE any_fld WITH any_fld
REPLACE vlf_1 WITH V_REPLACE( "testing", vlf_1 )
COMMIT
REPLACE vlf_2 WITH V_REPLACE( "testing", vlf_2 )
Ok, so what is going on? Understanding the sequence of events that
occurs during a REPLACE is critical to understanding the apparent
paradox in the examples above.
Remembering that the arguments of a function or command are all
evaluated before the function itself is called, the syntax
REPLACE vlf_fld WITH V_REP( "Testing an indexed replace", vlf_fld )
first executes FlexFile's V_REPLACE() and then executes Clipper's
REPLACE.
This affects indexing because Clipper always evaluates the index key
twice. Once at the begining of the first of one or more REPLACEs (to
remember which index key has to be removed from the index) and the
second after any of several "commit" commands such as COMMIT, SKIP,
SEEK, GOTO, etc. (to calculate and insert a new index key). However, if
FlexFile already deleted the old information pointed to by vlf_fld
before CLIPPER began to execute the REPLACE code, then the first
evaluation of the index key occurs after its data has already been
deleted by FlexFile.
I realize this is confusing so I will try again in steps:
┌─ step 2 ┌─ step 1
│ │
REPLACE vlf_fld WITH V_REPLACE( "testing", vlf_fld )
│ │
└─ step 4 └─ step 3
step 5
1. FlexFile deletes the data associated with the pointer-field vlf_fld
that is in the file before the REPLACE.
2. FlexFile puts away the text "testing" in an appropriate location.
3. FlexFile returns a six byte pointer-field which is immediately passed
to REPLACE. This pointer-field is different than that of step 1 and
this new pointer-field is not yet in the file (it is being stored in
memory for the moment).
4. Clipper begins executing its REPLACE code with the string passed to
it from step 3.
5. Clipper evaluates the index and stores the evaluated value in a
buffer. This sets up the error. The index key should evaluate to the
data pointed to by the vlf_fld which is still in the file. However,
FlexFile deleted that data in step 1. Therefore, the V_RETRIEVE() in the
index key will return a null string and your PAD function will change
this to 15 bytes of spaces.
No Clipper error actually occurs until the first COMMIT, SKIP, SEEK,
etc. It is at this point that Clipper does the work on the index. The
first thing Clipper does is delete the old index key (which was put into
a memory buffer in step 5). However, Clipper will not be able to find
an index key which matches the 15 spaces in the memory buffer and,
therefore, assumes that the index is corrupted.
So, the way to resolve this is to have a "dummy" replace that will force
Clipper to evaluate the index key BEFORE FlexFile deletes the key. The
thing that may be confusing is that Clipper does not care which field is
in the first REPLACE. It is simply a fact that the first REPLACE
evaluates the "before" index key regardless of whether the REPLACE even
affects the index key. It is also a fact that no other index action
occurs until a "commit" type command. At that point the "before" key is
deleted from the index and the "after" key is inserted into the index.
FlexFile Networking
FlexFile typically requires no special network considerations
beyond Clipper's RLOCK() and FLOCK(). That is to say, if you
acquire an RLOCK() on a record in a DBF file that has a
pointer-field, no other user will have write access to that
record. Because Clipper insists on an RLOCK() being acquired
before a REPLACE when a file is used non-exclusively, this
will suffice to protect the information to which the
pointer-field is related.
FlexFile's internal tables lock and unlock automatically, and
require no programmer consideration.
There is an additional network features which was incorporated
into FlexFile in order to aid the programmer in implementing
network compatible code.
Every time a pointer is replaced with a new pointer, the new
pointer is guaranteed to have a different value even though it
may be pointing to the same position within the file.
For example, if you have variable length data that is 30 bytes
long, residing at offset 5000 in the DBV file, and you replace
that data with new data that is also 30 bytes long, it is
highly probable that FlexFile will put the new data exactly
where the old data was (at offset 5000). However, the
pointer-field that the V_REPLACE() function returns will be
different than the one that pointed to the data before the
REPLACE.
This feature was implemented so that the programmer can assign
the pointer- field to a variable, then, without locking the
DBF record in which the pointer- field is located, modify the
pointer's data in memory. Then when the user is ready to
replace the old data with the modified data, the programmer
can compare the pointer-field in memory with the DBF's
pointer-field in order to ascertain whether the data was
modified by another user while the current user was working on
the copy of it in memory. For example:
// Open the DBF and its related DBV
USE Shareit.dbf
IF V_USE( 'Shareit.dbv' ) == -1
? "I could not open the Shareit.dbf file."
? "I encountered error: " + STR( V_ERROR() )
QUIT
ENDIF
// Save the pointer <vlf> to a variable
save_vlf = shareit->vlf
// Retrieve the data from the DBV file into a memory var.
// The DBF lock is momentary.
DO WHILE !RLOCK()
ENDDO
memo_data = V_RETRIEVE( vlf )
UNLOCK
// Edit the data.
memo_data = MEMOEDIT(memo_data...)
// Lock the record and replace the data if the
// data has not been modified since the pointer was
// saved. Again, the lock is momentary.
DO WHILE !RLOCK()
ENDDO
IF save_vlf == shareit->vlf
REPLACE vlf WITH V_REPLACE( memo_data, vlf )
ELSE
? "Data has been modified by another user!"
INKEY(0)
ENDIF
UNLOCK
Although the Clipper locking technique shown is crude (at
best), the comparison of the saved version of the pointer with
the DBF pointer-field is a technique that many programmers
prefer. The advantage is, of course, that you only need to
obtain two momentary locks rather than one long lock (during
which your user invariably goes on a lunch break). The
disadvantage is that sometimes the user will have to retype an
entry.
FlexFile's internal locking uses the DOS 3.x locking
mechanism. Therefore, if you are using a lower version of DOS
or do not have SHARE.EXE loaded, FlexFile will ignore any
attempts to lock the file and proceed as if the lock was
successful.
ARRAYS
FlexFile supports an implementation of strongly typed
multi-dimensional arrays. This array system is not to be
looked at as a replacement for the Clipper array system.
Clipper's array system is very powerful and it should not be
construed that FlexFile arrays will supersede the Clipper
system. Rather, FlexFile arrays should be used when the array
structure is regular, all the elements are of the same type,
and the array is very large.
UNDER NO CIRCUMSTANCE SHOULD YOU USE FLEXFILE'S "S"TRING TYPE ARRAY IN
CLIPPER 5.0X. EACH STRING ELEMENT OF THE ARRAY IS NOT HANDLED BY THE
VMM SYSTEM, AND, THEREFORE, THE CLIPPER COUNTERPART WILL TAKE LESS
MEMORY. ALL OTHER FLEXFILE ARRAYS CAN BE SWAPPED, WHEN NECESSARY, BY
CLIPPER'S VMM SYSTEM.
Why Use FlexFile Arrays
FlexFile arrays are the only arrays to use if your array must
exceed Clipper's limit of 4029 elements. For example, a
FlexFile array of logicals allows over 500,000 elements. In
addition, each element of FlexFile array is at least six bytes
smaller than its Clipper counter part and at most thirteen
bytes and 7 bits smaller. See Appendix B for a list of the
sizes of each type of FlexFile array.
Clipper sees Flexfile arrays as regular variables. Therefore,
you pass a Flexfile arrays by value (unless you
pass-by-reference using the @ symbol) and you can return a
FlexFile array as though it were a simple Clipper variable.
Because these arrays are typically large, it is best to pass
them by reference in order to avoid duplicating their content
in memory.
The one exception to this is FlexFile's string type arrays.
These arrays pose a unique problem which is discuss below (See
FlexFile String Arrays).
Array indexes
A "subscript" or an "index" refers to a particular element of
an array. For example, Clipper refers to the third element of
a single dimensional array as ArrayName[3]. In this case, [3]
is a subscript of the array ArrayName.
Subscripts may be passed using either of two different
methods. Both methods are valid at any time. The method to
use depends largely on whether your array is single or
multi-dimensional.
If the array is a single dimensional array, then you define
and index position into that array exactly as you would
imagine. For example, if you want to store 98.6 to the fifth
element of a single dimensional floating point type array, the
following line would be fine:
nTemperature = 98.6
nPatient = 5
A_STORE( aTemps, nTemperature, nPatient )
However, if the array is multi-dimensional it is necessary to
pass the index via the A_() function. For example, if you
want to store the temperature of the fifth patient on the
third day:
A_STORE( aTemps, 98.6, A_( 5, 3 ) )
It is not wise to try to figure out the actual integer offset
of an element even though FlexFile would accept it; FlexFile
does this very fast on its own. In addition, remember element
A_( 5, 3 ) is not the same element as A_(3, 5), and rarely, if
ever, will either array index refer to the fifteenth element
as it is laid out in memory.
Understanding FlexFile Arrays
FlexFile arrays are similar to the arrays in languages such as
C or Pascal. The array is one block of memory (with the
exception of string arrays), and each element in the array
must be of the same type.
There is an analogy relating multi-dimensional arrays to a set
of encyclopedias that helps in understanding and describing
their use. This analogy is especially helpful in
understanding arrays that span into the fourth dimension.
Imagine that you have a character array. First, do not
confuse character arrays and string arrays. A string array is
an array of pointers that point to strings of varying length
that reside in random positions in memory. The string array
is actually an array of pointers and not an array of strings.
Each element points to a string of any length.
A character array is a matrix of one byte storage locations
where each byte can hold one ASCII character. In the analogy
of the encyclopedias, each letter is an element, a group of
letters that form words make up one row of text. That one
line implies the first dimension. Likewise, A group of lines
that makes up one page implies the second dimension, a group
of pages that makes up one volume implies the third dimension,
and all the volumes in the set of encyclopedias implies the
fourth dimensions. We could continue the analogy into the
fifth dimension: a set of Britannica's next to The World Book
set implies a magnitude of two in the fifth dimension.
You will often see, in the Array Function Reference chapter,
the concept of "wrapping". For example, A_SCAN() will wrap at
the end of one line to the beginning of the next, and at the
end of one page it will wrap to the top of the next page. All
functions that work with wrapping look at the array as it is
laid out in memory.
It is important to dimension your arrays with this
understanding in mind: the last dimension listed in the
declaration varies the smallest step in memory. To declare
the array for an encyclopedia you would want to declare it as
follows:
vols := 26
pages := 150
lines := 80
chars := 95
WorldBook = A_DECLARE( 'C', vols, pages, lines, chars )
This declaration of a character array is actually illegal
because the array would exceed memory, however, it is
important to see how and why the dimensions are in the order
they are in. The <chars> variable is last in order to insure
that each character is next to its proper neighbor in memory.
As far as A_STORE()ing and A_RETRIEVE()ing each character, the
layout makes little difference. However, when you need to do
something like an A_FILL() (for a blank line), this layout
becomes critical.
Lets say, for example, we want the fifth and sixth lines on
the fifteenth page of the fourth volume to be filled with
spaces.
A_FILL( WorldBook, ' ', A_( 4, 15, 5, 1 ), 2 * lines )
This function fills the 160 characters following the specified
location. Because the fill always begins at a point in memory
and moves to the next set of contiguous positions in memory,
it becomes critical how the array was declared. For example,
if we switch the order of the <lines> and <chars> in the
declaration, and then perform the A_FILL(), the fill would put
one space on each line for 160 lines instead of one space for
each character for 160 characters.
FlexFile String arrays (pointer arrays)
Arrays of strings in FlexFile pose a peculiar problem. When
the array is declared, FlexFile allocates memory for four
bytes per element. These four bytes originally point to
nothing (null). When you A_STORE() a string to an element,
however, new memory is allocated for the string. This memory
does not reside within the array, but rather, at some random
location in memory. FlexFile knows where this is because it
stores a pointer in the array which points to the new data.
A problem arises because the array itself is a Clipper
variable and has the scope of a Clipper variable. If you
release this variable or allow it to fall out of scope,
Clipper does not know that it was pointing to your strings,
and so, Clipper releases the array of pointers without
releasing the strings that it points to. In order to avoid
this, it is necessary to A_KILL() a string array before
allowing it to fall out of scope or be released.
V_ALIAS()
Return the alias of the current or a specified DBV work area
───────────────────────────────────────────────────────────────────────────────
Syntax
V_ALIAS( [<nDBVArea>] ) -> cDBVAlias
Arguments
<nDBVArea> is any valid DBV work area.
Returns
V_ALIAS() returns the alias of the DBV work area <nDBVArea>. If no
<nDBVArea> is specified, then the alias of the current work area
is returned.
Description
V_ALIAS() is used to return the name of a DBV work area. This name
is either the name of the DBV file or the name specified as the
second parameter in the V_USE() function. If there is no DBV file
open in the <nDBVArea>, V_ALIAS() will return a null string ("").
Any function which accepts a DBV area as a parameter can also
accept an alias in place of the area.
The alias is always returned in capital letters regardless of how
it was passed to V_USE().
Notes
■ Do not confuse Clipper's alias names with FlexFile's; they are
mutually exclusive. For example, you can have a DBF file with an
alias of "MYFILE" and simultaneously have a DBV file with the same
alias.
Examples
// Open two DBV files in two new areas.
V_SELECT(1)
V_USE( "file1", "file_one" )// Creates if not existing
V_SELECT(2)
V_USE( "file2" ) // Creates if not existing
? V_ALIAS() // Returns: file2
? V_ALIAS(1) // Returns: FILE_ONE
See Also: V_SELECT() V_USE() V_FILES() V_FILE()
───────────────────────────────────────────────────────────────────────────────
V_BUFFERS()
Declare or retrieve the number of DBV file buffers
───────────────────────────────────────────────────────────────────────────────
Syntax
V_BUFFERS( [<nBuffers>] ) -> nBuffers
Arguments
<nBuffers> is an integer value indicating a number of 1K buffers.
The default (which is also the minimum value) is 3 buffers.
Returns
V_BUFFERS() returns an integer indicating the previous setting of
the number of 1K buffers.
Description
FlexFile manages all file input and output through a set of 1K
buffers. Because there is such a diversity in buffered I/O from
machine to machine, FlexFile offers the ability to customize the
amount of space used for this purpose.
If you are running with a disk cache or are tight on memory, leave
the buffers at their default (and minimum) value of 3. If you have
the space and would like to increase performance, increase the
number of buffers.
Notes
■ The buffers may only be changed when no DBV files are in use.
Examples
// Set FlexFile's buffers to 5K
V_BUFFERS(5)
V_USE( "file" ) // Open a file.
// This line has no effect because files are open.
V_BUFFERS(10)
? V_BUFFERS() // Result: 5
See Also: V_FILES() V_USE()
───────────────────────────────────────────────────────────────────────────────
V_CLOSE()
Close a DBV type file
───────────────────────────────────────────────────────────────────────────────
Syntax
V_CLOSE( [<nWorkArea> | <cAlias>] )
Arguments
<nWorkArea> or <cAlias> is the DBV area or alias to close. If none
is specified the file in the current DBV area is closed.
Returns
None.
Description
V_CLOSE() closes a FlexFile DBV type file. This is equivalent to
V_USE() with no arguments.
Notes
■ This function does not affect and is not affected by any
Clipper data base functions or commands.
Examples
// Open a DBV type file in FlexFile's area 1.
V_SELECT(1)
V_USE( "file" )
// Select a second area just for the heck of it.
V_SELECT(2)
// Close the file in area one.
V_CLOSE(1)
// Close a file (if one is open) in area 2
V_CLOSE()
See Also: V_USE() V_CLOSEALL()
───────────────────────────────────────────────────────────────────────────────
V_CLOSEALL()
Close all open DBV type files
───────────────────────────────────────────────────────────────────────────────
Syntax
V_CLOSEALL()
Arguments
None.
Returns
None.
Description
V_CLOSEALL() closes all open DBV type files in all open DBV work
areas. In addition, the cache described by V_BUFFERS() and
V_FILES() is released.
Notes
■ This function does not affect and is not affected by any
Clipper data base functions or commands.
Examples
// Open a DBV type file in FlexFile's area 1.
V_SELECT(1)
V_USE( "file" )
// Open a second file in the next available area.
V_USE( "file2", , "NEW" )
// Close all open files.
V_CLOSEALL()
See Also: V_CLOSE() V_USE()
───────────────────────────────────────────────────────────────────────────────
V_COMMIT()
Commit the DOS buffers to disk.
───────────────────────────────────────────────────────────────────────────────
Syntax
V_COMMIT( [<nArea> | <cAlias>] ) -> lSuccess
Arguments
<nArea> or <nAlias> is the DBV work area or alias whose buffers
will be flushed. Omitting this parameter causes all open DBV file
buffers to be flushed.
Returns
V_COMMIT() returns a (.T.) on success. Otherwise, it returns
(.F.).
Description
DOS "buffers" disk I/O in order to speed up disk access. The
downside to this feature is that the data in the buffers can be
lost if the machine "hangs" or is powered down before the buffers
are copied to disk. This problem was addressed in DOS version 3.3
and later by allowing a call which forces DOS's buffers to be
written to disk. V_COMMIT() makes this call to DOS as described
below.
By default, FlexFile automatically commits all DOS buffers after
every write operation. If you like this behavior, you should never
need to call V_COMMIT(). This is the safest method but also the
slowest. In order to change this default behavior, the
V_SET_COMMIT() function can be passed a logical (.T.) or (.F.) to
turn on/off the automatic flushing feature. If automatic flushing
is turned off, you must call V_COMMIT() explicitly in order to
flush the DOS buffers as described below.
If no parameter is passed, V_COMMIT() flushes all open DBV work
areas to disk.
If a numeric DBV work area or a character alias is passed,
V_COMMIT() will flush the buffers associated with the work area
specified.
┌──────────────────────── WARNING ─────────────────────────┐
│ V_COMMIT and V_SET_COMMIT() are based on functions │
│ provided in DOS 3.3 and later. Calls made to these │
│ functions on an earlier version of DOS will have no │
│ affect. │
└──────────────────────────────────────────────────────────┘
Example
// Turn off automatic flushing in order to speed up operation.
V_USE( "temp" )
V_SET_COMMIT(.f.)
DO WHILE !EOF()
IF DELETED()
REPLACE vlf_fld WITH V_DELETE( vlf_fld )
ENDIF
SKIP
ENDDO
PACK
// Flush the buffers and turn automatic flushing back on.
V_COMMIT()
V_SET_COMMIT(.T.)
See also: V_SET_COMMIT() V_REPLACE() V_DELETE()
───────────────────────────────────────────────────────────────────────────────
V_DECRYPT()
Decrypt a variable that has been encrypted with V_ENCRYPT()
───────────────────────────────────────────────────────────────────────────────
Syntax
V_DECRYPT( <cEncrypted_string> ) -> cOriginal
Arguments
<cEncrypted_string> is a variable previously encrypted with
the V_ENCRYPT() function.
Returns
V_DECRYPT() returns a string as it was before a call to V_ENCRYPT().
Description
V_DECRYPT() will decrypt a string that has been previously encrypted
with the V_ENCRYPT() function. The string will only be decrypted
properly if the V_SETKEY() function has been called with the same key
that was in effect when the V_ENCRYPT() originally scrambled the
<cEncrypted_string>.
Examples
LOCAL cSecure, cOriginal
// Set the key to my dog's name.
V_SETKEY( "Tally" )
// Encrypt a character string. Store the result in <cSecure>.
cSecure = V_ENCRYPT( "Just try to decode <cSecure> without my dog." )
// Later, decode the secured string with the help of my golden.
// The call to V_SETKEY() is redundant because the program has not
// terminated and it has not been set to a different V_SETKEY().
V_SETKEY( "Tally" )
cOriginal = V_DECRYPT( cSecure )
See Also: V_SETKEY() V_ENCRYPT()
───────────────────────────────────────────────────────────────────────────────
V_DELETE()
Delete one field from a DBV file
───────────────────────────────────────────────────────────────────────────────
Syntax
V_DELETE( <cPointer>,
[<nArea> | <cAlias>] ) -> cNull
Arguments
<cPointer> is a six byte pointer-field (FlexFile's version of a
memo-field).
<nArea> or <nAlias> is the DBV work area or alias with which
<cPointer> is related.
Returns
V_DELETE() returns a null string ("").
Description
V_DELETE() releases the space pointed to by <cPointer>. This space
is then available for use by the next call to V_REPLACE() or any
other function that requires DBV space. It is important to
remember that there is no V_RECALL(). That is, once a DBV has been
V_DELETE()d, that field is no longer recoverable.
When you delete a record in a DBF file and you want to be able to
recall that record (and its related VLF), do not use V_DELETE().
Instead, just DELETE the record in the DBF. When you are ready to
PACK the DBF, run a routine similar to the example below.
┌──────────────────────── WARNING ─────────────────────────┐
│ V_DELETE() deletes the data associated with <cPointer>. │
│ Attempting to use <cPointer> after this function deletes │
│ the data that it points to can corrupt the DBV file. │
└──────────────────────────────────────────────────────────┘
Notes
■ V_DELETE() is permanent; there is no V_RECALL().
■ V_DELETE() is not necessary before a V_REPLACE(). The second
parameter of V_REPLACE() is a pointer whose data space will be
released before the REPLACE.
Examples
// Open a DBF and its related DBV
USE dbf_file
V_USE( "dbv_file" )
// Store some data in the DBV and a pointer to the
// data in a six byte pointer-field in the DBF
REPLACE vlf WITH V_REPLACE( "This string is doomed", vlf )
// Now delete that string.
REPLACE vlf WITH V_DELETE( vlf )
// Note: It is important to erase the pointer stored in
// vlf once its data has been deleted. Because
// V_DELETE() returns a null string, REPLACEing <vlf>
// guarantees that the pointer-field vlf will be
// destroyed.
See Also: V_REPLACE() V_STUFF()
───────────────────────────────────────────────────────────────────────────────
V_ENCRYPT()
Encrypt a Clipper string variable
───────────────────────────────────────────────────────────────────────────────
Syntax
V_ENCRYPT( <cString> ) -> cGobbleTgook
Arguments
<cString> is any valid Clipper character string.
Returns
V_ENCRYPT() returns an ecrypted string with the same length as
the original.
Description
V_ENCRYPT() encrypts a Clipper string based on the current key setting
(see V_SETKEY()). The string can later be V_DECRYPT()ed by using the
same V_SETKEY() setting.
V_ENCRYPT() encrypts a Clipper string so that it is not human readable.
The encryption algorithm has not been designed to any particular
specification, however, as the author of the algorithm, I would not
want to be faced with the task of decrypting a string without the
<key> with which it was encrypted (see V_SETKEY()).
Examples
LOCAL cSecure, cOriginal
// Set the key to my dog's name.
V_SETKEY( "Tally" )
// Encrypt a character string. Store the result in <cSecure>.
cSecure = V_ENCRYPT( "Just try to decode <cSecure> without my dog." )
// Later, decode the secured string with the help of my golden.
// The call to V_SETKEY() is redundant because the program has not
// terminated and it has not been set to a different V_SETKEY().
V_SETKEY( "Tally" )
cOriginal = V_DECRYPT( cSecure )
See Also: V_SETKEY() V_DECRYPT()
───────────────────────────────────────────────────────────────────────────────
V_ERROR()
Report specific error codes from the DBV system
───────────────────────────────────────────────────────────────────────────────
Syntax
V_ERROR() -> nErrorCode
Arguments
None.
Returns
V_ERROR() returns the FlexFile error code as a numeric integer.
Description
V_ERROR() is an error reporting function that will return the most
recent error code that FlexFile set. If a failed operation has no
FlexFile error code check the DOS error code by using Clipper's
DOSERROR() function.
For a complete list of error codes and their descriptions refer to
Appendix B.
Examples
// Trying to V_REPLACE() data in an area that does not have a
// file open causes an error.
V_FILES(4)
USE dbf_file // Valid DBF file is opened.
V_SELECT( 4 ) // No DBV file is open in area 4.
// V_REPLACE() will return (.F.) because there is no file
// open in area 4. Because vlf is a six byte character
// field, a runtime "data type mismatch" is produced.
REPLACE vlf WITH V_REPLACE("Data type mismatch", vlf)
// In errorsys.prg...
? V_ERROR() // Returns 7301.
// Appendix B: DBV file not open.
See Also: errcodes.ngo:"Error Codes Introduction"
───────────────────────────────────────────────────────────────────────────────
V_EXCLUSIV()
Establish exclusive or shared use of a DBV type file
───────────────────────────────────────────────────────────────────────────────
Syntax
V_EXCLUSIV( <lToggle> ) -> lSetting
Arguments
<lToggle> is a logical value. If <lToggle> is (.T.), then
subsequent files opened with V_USE() will be opened exclusively.
If <lToggle> is (.F.), then subsequent files opened with V_USE()
will be opened in shared mode.
Returns
The current setting of V_EXCLUSIV().
Description
In a network environment, V_EXCLUSIV() determines whether the
V_USE() function specified without the <lExclusive> parameter will
be opened in shared or exclusive mode. If <lToggle> is (.T.) (the
default), other users cannot V_USE() the same file until it has
been closed.
If <lToggle> is (.F.), all subsequent files opened by V_USE() without the
<lExclusive> parameter will be opened in shared mode. Access by
other users must be controlled programmatically. Typically, this
can be done with Clipper's RLOCK() and FLOCK() functions. That is,
if the pointer to a DBV is kept in a DBF file, then RLOCK()ing the
record containing the pointer will prevent other users from
accessing that field.
Refer to Network Programming for more information.
Notes
■ Error handling: Attempting to V_USE() a file which has been
opened exclusively by another user causes V_USE() to return a -1.
V_ERROR() will return an error code indicating this condition.
Examples
// Set up the environment so that all files are opened
// in shared mode.
#define EXCLUSIVE (.T.)
V_EXCLUSIV( !EXCLUSIVE )
// This file will be opened in shared mode.
IF V_USE( "mainfile" ) == -1
? "Error: " + str( V_ERROR(), 4 )
QUIT
ENDIF
// This V_USE() overrides the global V_EXCLUSIV() setting
// and opens the file exclusively.
V_USE( "tempfile", , "NEW", EXCLUSIVE )
// Close all files.
V_CLOSEALL()
See Also: V_USE() network.ngo:"Network Programming"
───────────────────────────────────────────────────────────────────────────────
V_FILE2VLF()
Copy a DOS file into a VLF
───────────────────────────────────────────────────────────────────────────────
Syntax
V_FILE2VLF( <cFileName>,
<cOldPointer> ) -> cNewPointer
Arguments
<cFileName> is the name of a disk file optionally including the
path and drive designators.
<cOldPointer> is a required six byte pointer-field or variable in
which you have previously stored a pointer to variable length
data. Passing six spaces causes FlexFile to assume that there is
no old data to be released before the new <exp> data is saved.
Returns
V_FILE2VLF() returns a pointer which should be stored in the
pointer- field with which the copied file is to be associated.
V_FILE2VLF() returns (.F.) on error. This causes the error system
to be invoked with a "Data Type Mismatch". Use V_ERROR() to fetch
the specific error value (see Appendix B for a table of error
descriptions).
A typical error from this function is "File not found". It is,
therefore, the responsibility of the programmer to test for this
condition before executing V_FILE2VLF().
Description
V_FILE2VLF() provides a mechanism for copying a DOS disk file into
a VLF. This is particularly handy in converting an application
which has used Clipper's MEMOWRITE() function.
V_FILE2VLF() is also handy for copying graphics images from disk
files into a DBV file. When used in conjunction with V_VLF2FILE()
a system can be implemented to handle graphics images which is
compatible with the file based graphics systems such as DGE,
SilverPaint, Flipper and the like.
┌────────────────────────── WARNING ──────────────────────────┐
│ V_FILE2VLF() deletes the data associated with <cOldPointer> │
│ before returning <cNewPointer>. Attempting to use │
│ <cOldPointer> after this function deletes the data that it │
│ points to can corrupt the DBV file. │
└─────────────────────────────────────────────────────────────┘
Notes
■ No additional memory is required to copy a file into a VLF. It
is, therefore, possible to import a file larger than 65K
(Clipper's variable size limit).
■ V_TYPE() will return "C" (character) for any VLF that has been
created by V_FILE2VLF(). This implies that V_RETRIEVE() will
return a character string.
Examples
// Open a DBF file and its related DBV file
// The dbf_file has a 12 byte field called "exe_name"
// and the usual 6 byte field called "vlf"
USE dbf_file
ZAP
INDEX ON exe_name TO dbf_file
V_USE( "dbv_file" )
// This may seem silly, but lets store the Clipper compiler
// in the DBV file.
APPEND BLANK
REPLACE exe_name WITH "CLIPPER.EXE"
// This will copy the Clipper.exe into a VLF.
REPLACE vlf WITH V_FILE2VLF( exe_name, vlf )
// Later when we want to execute Clipper (you better have
// lots of memory).
SEEK "CLIPPER.EXE"
? V_VLF2FILE( "TEMP.EXE", vlf )
? V_ERROR()
RUN temp
See Also: V_DBV2FILE() V_RETRIEVE()
───────────────────────────────────────────────────────────────────────────────
V_FILENAME()
Return the file name of a file opened in a specified area
───────────────────────────────────────────────────────────────────────────────
Syntax
V_FILENAME( [<nArea> | <cAlias>] ) -> cFileName
Arguments
<nArea> or <cAlias> refers to the number or name of the target DBV
work area. If not specified, V_FILENAME() returns the file name
from the currently selected DBV work area.
Returns
V_FILENAME() returns the DOS file name of the DBV file opened in
the specified area. If no file is open in the specified area,
V_FILENAME() returns a null string ( "" ).
Description
V_FILENAME() is used to retrieve the DOS file name that refers to
the DBV file. This name will not include the drive or path
designators.
This function is provided specifically for debugging where it is
sometimes insufficient to only have access to the alias.
Examples
// Show the name of the file (not the alias)
V_SELECT(1)
V_USE( "pic_file.dbv", "pictures" )
? V_FILENAME() // Returns: PIC_FILE.DBV
? V_ALIAS() // Returns: PICTURES
V_SELECT(2)
? V_FILENAME( "PICTURES" )// Returns: PIC_FILE.DBV
See Also: V_ALIAS()
───────────────────────────────────────────────────────────────────────────────
V_FILES()
Set the maximum number of DBV files to be in use concurrently
───────────────────────────────────────────────────────────────────────────────
Syntax
V_FILES( [<nMaxFiles>] ) -> nCurrentMax
Arguments
<nMaxFiles> is the maximum number of DBV type files that can be in
V_USE() concurrently. If none is specified, V_FILES() will simply
return the current setting.
Returns
V_FILES() returns the current setting of the maximum file count.
Description
V_FILES() offers the programmer the opportunity to set the maximum
number of files that the program will have in use at any one time.
This feature allows FlexFile to keep the memory used for the file
headers in a contiguous block which avoids memory fragmentation.
Each file requires 84 bytes for the header information. Therefore,
multiply the <nMaxFiles> by 84 in order to calculate the total
memory requirement for the files opened in your system.
Notes
■ The maximum number of files cannot be changed unless all DBV files are
closed.
■ This function is independent of the V_BUFFERS() setting.
Examples
// Set FlexFile's maximum file count to 2 files
V_FILES(2)
V_USE( "file1" ) // Open first file.
V_USE( "file2", ,"NEW" ) // Open second file
// in a new area.
// Attempt to open a third file in a new area fails.
IF V_USE( "file3", ,"NEW" ) == -1
? "Too many files in use."
? V_ERROR() // Result: 8001
ENDIF
// This line has no effect because files are open.
V_FILES(5)
? V_FILES() // Result: 2
See Also: V_BUFFERS() V_USE()
───────────────────────────────────────────────────────────────────────────────
V_FILLARR()
Fill a Clipper S87 array with contents stored to a DBV file
───────────────────────────────────────────────────────────────────────────────
Syntax
V_FILLARR( <aTarget>,
<cPointer>,
[<nArea> | <cAlias>] )
Arguments
<aTarget> is the Clipper S87 array to be filled.
<cPointer> is a six byte pointer-field that points to the DBV
containing a Clipper type array.
<cAlias> or <nArea> is a DBV file alias or area specified when the
file was V_USE()d. If omitted, the pointer will point into the
currently selected DBV area.
Returns
None. However, <aTarget> is modified.
Description
V_FILLARR() is used to fill an array when using the Clipper S87
compiler (See V_RETRIEVE() for retrieving an array in an application
compiled with Clipper 5.0). The <aTarget> array must exist and will be
filled until <aTarget> has no remaining elements or the array pointed
to by <cPointer> has no more elements (which ever limit is reached
first).
Use the V_LEN() function to dimension the array immediately before
V_FILLARR() (see examples below).
The ability to save Clipper arrays is one of the most powerful
features of FlexFile. Essentially, it simulates Variable Length
Records as well as creating a three dimensional effect in your
data base files.
For example, picture a student master DBF file with the fields
LastName, FirstName, Addr, Major, and Minor. If you decide to
expand the file and include the sports, clubs and associations
that the student is involved in, you have two choices. You can
create a new related file (with a new index), or you can insert
several new fields in the master DBF for each category, limiting
the entries possible in any one category and wasting space on
students who do not have entries in one or more of the categories.
With FlexFile, however, you can add just one field in the master
file and save an array of any length to this field. Not only do
you eliminate a file, its index, and its relation, but also, one
function call can load arrays with the new data eliminating the
need to code "scatter" and "gather" routines.
Notes
■ It is not necessary to use this routine to retrieve a FlexFile
type array.
Examples
&& S'87 compiler only. See V_RETRIEVE() for Clipper 5.0
&& Open a DBF and a related DBV file
USE dbf_file
V_USE( "dbv_file" )
DECLARE aNames[3]
aNames[1] = "Tania"
aNames[2] = "Mary"
aNames[3] = "Allison"
&& Store the array to the DBV file and its pointer to the DBF
REPLACE vlf WITH V_REPLACE( aNames, vlf )
&& Declare the new array to the proper size.
DECLARE aNewNames[ V_LEN( vlf ) ]
&& Fill the array with the data.
V_FILLARR( aNewNames, vlf )
&& Show that it worked
? aNewNames[1] && Result: Tania
See Also: V_REPLACE() V_RETRIEVE() V_LEN()
───────────────────────────────────────────────────────────────────────────────
V_GETWNDRC()
Return the rows or columns from a Proclip window stored in a DBV file
───────────────────────────────────────────────────────────────────────────────
Syntax
V_GETWNDRC( <cPointer>, <cRowOrCol> ) -> nValue
Arguments
<cPointer> is a six byte pointer-field (FlexFile's version of a
memo-field).
<cRowOrCol> is either an 'R' or a 'C' (non case sensitive)
requesting the row or column respectively.
Returns
V_GETWNDRC() returns the rows or columns of a Proclip window in a
DBV file much like Proclip's WNDVROWS() and WNDVCOLS() would.
Description
V_GETWNDRC() is used to query a Proclip window that has been saved
to a DBV. The rows and columns are the virtual rows and columns
and not the rows and columns of a viewport that may have been
active when the window was V_REPLACE()d.
Examples
// Create a proclip window
pc_handle = WNDCREATE( 5, 10 )
// Save the window to a DBV file
REPLACE vlf WITH V_REPLACE( pc_handle, , , 'P' )
// Show the rows and columns of the window.
? V_GETWNDRC( vlf, 'R' ) // Result: 5
? V_GETWNDRC( vlf, 'C' ) // Result: 10
See Also: V_REPLACE() V_TYPE()
───────────────────────────────────────────────────────────────────────────────
V_HANDLE()
Return the DOS file handle for a DBV file.
───────────────────────────────────────────────────────────────────────────────
Syntax
V_HANDLE( [<nArea> | <cAlias>] ) -> nHandle
Arguments
<nArea> or <cAlias> refers to the number or name of the target DBV
work area. If not specified, the current DBV work area is assumed.
Returns
V_HANDLE() returns a numeric integer whose value is the DOS file handle
for the specified DBV work area.
Description
V_HANDLE() can be used in conjunction with Clipper's Fxxxx() class of
low level file functions. Great care must be taken when using FWRITE()
to assure that the bounds of the VLF are not overwritten. It is far
safer and usually sufficient to use V_RETRIEVE() and V_POKE() to manage
low level access to a VLF.
┌───────────────────────── WARNING ───────────────────────────┐
│ Extreme care must be taken when using FWRITE() on a DBV │
│ type file. Overwriting the bounds of a VLF by one byte can │
│ corrupt the entire file. │
└─────────────────────────────────────────────────────────────┘
Examples
#include "fileio.ch"
LOCAL buffer
// Open a DBF file and its related DBV file
USE dbf_file
V_USE( "dbv_file" )
FSEEK( V_HANDLE(), V_PTR( vlf_fld ), FS_SET )
buffer = SPACE( V_LEN( vlf_fld ) )
FREAD( V_HANDLE(), @buffer, LEN(BUFFER) )
// The above code will fetch a VLF without file locking or
// error checking just like the following one line of code:
// buffer = V_RETRIEVE( vlf_fld )
See Also: V_POKE() V_RETRIEVE() V_PTR() V_LEN()
────────────────────────────────────────────────────────────────────────────────
V_ISOPEN()
Determine if a file is open in an area.
────────────────────────────────────────────────────────────────────────────────
Syntax
V_ISOPEN( [<nArea> | <cAlias>] ) -> lStatus
Arguments
<nArea> or <cAlias> refers to the number or name of the target DBV
work area. If not specified, the current DBV work area is assumed.
Returns
V_ISOPEN() returns (.T.) if there is a file open in the current or
specified DBV work area. Otherwise, it returns (.F.).
If a character alias is passed, V_ISOPEN() will return true if the
alias is valid in any DBV work area. If the character alias is
not valid for any DBV work area, V_ISOPEN() will return (.F.).
Description
V_ISOPEN() tests the current or specified area returning a logical
(.T.) if a file was found to be open in that area. This function
is important because the behavior of V_SELECT() does not allow
this test as SELECT() does in Clipper (see V_SELECT()).
Examples
V_FILES(5) // Set maximum open files to five
V_USE( "file1" ) // Open files in areas one and three.
V_SELECT(3)
V_USE( "file2" )
FOR x = 1 TO 5
? V_ISOPEN(x), x // Results: .T. for x=1 and x=3
NEXT
IF V_ISOPEN( "bogus" ) .OR. !V_ISOPEN( "file1" )
? "This is unreachable code."
ELSE
? "Every thing went as expected!"
ENDIF
See also: V_SELECT(), V_ALIAS(), V_USE()
───────────────────────────────────────────────────────────────────────────────
V_LEN()
Return the length of data stored in a DBV
───────────────────────────────────────────────────────────────────────────────
Syntax
V_LEN( <cPointer>,
<nArea> | <cAlias> ) -> nCount
Arguments
<cPointer> is a six byte pointer-field (FlexFile's version of a
memo-field).
<nArea> or <cAlias> refers to the number or name of the target DBV
work area. If not specified, the current DBV work area is assumed.
Returns
V_LEN() returns the length of a character string or the number of
elements in an array as a numeric integer. V_LEN() returns only
the number of elements in the first dimension of a Clipper 5.0
array.
Description
With a character string, V_LEN() returns the number of bytes of
the data associated with <cPointer>. This count will include any
imbedded null bytes (CHR(0)).
With an array, V_LEN() returns the number of elements in the first
dimension of the array.
For all other data types, V_LEN() returns 0.
Examples
// Declare and assign two local variables to be used.
LOCAL cString, aNames := { "Sandra", "Beth", "Pirko" }
cString = "This is a test"
// Open a DBF file and its related DBV file
USE dbf_file
V_USE( "dbv_file" )
// Store a string to record 1 and an array to record 2
REPLACE vlf WITH V_REPLACE( cString, vlf )
SKIP
REPLACE vlf WITH V_REPLACE( aNames, vlf )
GO TOP
? V_LEN( vlf ) // Result: 14
SKIP
? V_LEN( vlf ) // Result: 3
See Also: V_TYPE() V_REPLACE() V_RETRIEVE() V_FILLARR()
───────────────────────────────────────────────────────────────────────────────
V_POKE()
Write character data into a character type VLF
───────────────────────────────────────────────────────────────────────────────
Syntax
V_POKE( <cPointer>,
<cData>,
[<nOffset>] )
Arguments
<cPointer> is a six byte pointer-field (FlexFile's version of a
memo-field).
<cData> is a character string to be written into the data pointed
to by <cPointer>. If the length of <cData> is greater than
V_LEN(<cPointer>) - <nOffset>, the data will be trucated.
<nOffset> is the offset within the data pointed to by <cPointer>
at which to begin writing <cData>. If not specified, <nOffset>
defaults to 1.
Returns
None.
Description
V_POKE() is used when you want to overwrite some or all of a
character type VLF. V_POKE() is different than V_STUFF() in that
it does not change or return <cPointer>. Instead, V_POKE()
modifies the data directly on the disk at its present location.
V_POKE() is different than V_REPLACE() in that it cannot extend
the length of the data pointed to by <cPointer> nor can it release
any space associated with <cPointer>.
Notes
■ V_POKE() is most useful when you know that the new data is the
same length (or a subset) of the old data.
Examples
// Open a DBF and a related DBV file.
USE Main.dbf
V_USE( "Main.dbv" )
// Replace can store any length data.
APPEND BLANK
REPLACE vlf_fld WITH V_REPLACE( "ABCDEFGH", vlf_fld )
// Poke some data into the file.
V_POKE( vlf_fld, "test", 3 )
? V_RETRIEVE( vlf_fld )// Result: ABtestGH
// Try to write past the end of the fields current length.
V_POKE( vlf_fld, "You cannot do much harm", 3 )
? V_RETRIEVE( vlf_fld )// Result: ABYou ca
See Also: V_REPLACE() V_STUFF()
───────────────────────────────────────────────────────────────────────────────
V_PTR()
Return the absolute offset (file pointer) of data stored in a DBV file
───────────────────────────────────────────────────────────────────────────────
Syntax
V_PTR( <cPointer> ) -> nOffset
Arguments
<cPointer> is a six byte pointer-field (FlexFile's version of a
memo-field).
Returns
V_PTR() returns the absolute offset of data pointed to by
<cPointer> in the DBV file associated with <cPointer>.
Description
For those who use Clipper's FOPEN() class of functions and who
would like direct access to the data in a DBV type file, FlexFile
provides V_PTR(). However, it is far safer to use V_RETRIEVE() and
V_POKE().
It must be noted that extreme care should be exercised when
writing data into the DBV file. You should never write data that
begins before V_PTR() or data which extends beyond V_LEN().
┌───────────────────────── WARNING ───────────────────────────┐
│ Extreme care must be taken when using FWRITE() on a DBV │
│ type file. Overwriting the bounds of a VLF by one byte can │
│ corrupt the entire file. │
└─────────────────────────────────────────────────────────────┘
Examples
// Open a DBF file and its related DBV file.
USE dbf_file
V_USE( "dbv_file" )
? V_PTR( vlf ) // Result if the data related to vlf
// is 11,222 bytes from the beginning
// of dbv_file.dbv: 11222
See Also: V_RETRIEVE() V_POKE()
───────────────────────────────────────────────────────────────────────────────
V_REPLACE() / V_REP()
Replace data stored in a DBV file with new data
───────────────────────────────────────────────────────────────────────────────
Syntax
V_REPLACE( <exp>,
<cOldPointer>,
[<cAlias> | <nArea>],
[<cDataType>]) -> cNewPointer
Arguments
<exp> is any valid clipper variable including single or
multi-dimensional arrays, FlexFile arrays, MemoEdit() strings,
SaveScreen() strings, any binary data stored in a Clipper
variable, as well as numerics, dates, and logicals, and Proclip
windows.
<cOldPointer> is a required six byte pointer-field or variable in
which you have previously stored a pointer to variable length
data. Passing six spaces causes FlexFile to assume that there is
no old data to be released before the new <exp> data is saved.
Refer to the discussion below for further explanation.
<cAlias> or <nArea> is a DBV file alias or area specified with
V_USE() or V_SELECT(). If omitted, the replace will occur in the
current DBV area.
<cDataType> is an expression which describes the data type of
<exp>. FlexFile will assume Clipper's data type if this parameter
is omitted. Only the first character of <cDataType> is significant
(e.g. "C" is equivalent to "Character" ).
This parameter is almost never necessary in this release of FlexFile.
This is because the type of the data being saved is usually
non-ambiguous. However, when saving either FlexFile's own strongly
typed arrays or Proclip(tm) windows, it is necessary to tell FlexFile
what you are saving.
This is because FlexFile strongly typed arrays appear to Clipper as
character data and Proclip window handles appear to Clipper as numeric
values. So, for example, if you save a Proclip window handle without
providing this parameter, FlexFile will save the handle instead of
the contents of the window.
Table: V_REPLACE() data type codes
┌──────────────────────────────────────────────┐
│ Character Description │
├──────────────────────────────────────────────┤
│ C Clipper character data │
│ N Clipper numeric │
│ L Clipper logical │
│ D Clipper date │
│ A Clipper array │
│ P Proclip window handle │
│ F FlexFile strongly typed array │
└──────────────────────────────────────────────┘
Returns
V_REPLACE() returns a pointer which should be stored in the
pointer- field with which the variable length data is to be
associated.
V_REPLACE() returns (.F.) on error. This causes the error system
to be invoked with a "Data Type Mismatch". Use V_ERROR() to fetch
the specific error value (see Appendix B for a table of error
descriptions).
Description
V_REPLACE() stores the variable length data defined by <exp> and
returns a six byte pointer <cNewPointer>. This pointer is the only
way FlexFile will ever be able to access the <exp> data; if this
pointer is lost the data in the DBV file will be inaccessible.
It is highly recommended, therefore, that the V_REPLACE() function
be imbedded in the same line of code as the Clipper REPLACE
command (see examples below). <cNewPointer> will then be stored
directly into a six byte pointer-field in the related DBF file.
V_REPLACE() first deletes the data associated with <cOldPointer>
freeing up the space, if any, that it was occupying. V_REPLACE()
then looks for the smallest available space to put the <exp> new
data. Finally, it returns <cNewPointer> to be used at any time by
V_RETRIEVE(), V_REPLACE(), V_LEN(), etc. In most cases, you will
be storing the <cNewPointer> in a DBF file (highly recommended).
In designing the associated DBF structure, you will define a
pseudo memo-field which will be a six byte (or more) character
type field. FlexFile refers to these fields as pointer-fields.
When a FlexFile pointer is stored in this field, the field will
have six bytes of ASCII characters from the "irrational" range
(128-255). This may appear as "garbage", but is actually a very
compact encoded structure. It is encoded only to insure that nulls
are never stored in your DBF file. After creating this field you
can ignore it exactly as you would a DBT memo-field. If you need
to know the offset of the data in the DBV file use the function
V_PTR().
The V_REPLACE() function replaces data in the current DBV area or
the <nArea>/<cAlias> area if specified. Attempting to use
<cOldPointer> which points into one DBV file to delete/replace
data in any other DBV file can be dangerous. Although there is a
degree of protection against this kind of programmer error, it is
possible that random data will be deleted from the target area
corrupting that file.
Incorrect: <fld1> is pointing to data in DBV area 1 but being used
to refer to data in DBV area 2.
V_SELECT(1)
REPLACE fld1 WITH V_REPLACE( "No problem yet", fld1 )
│
└─────────────────────────────────────────────────┐
V_SELECT(2) │
REPLACE fld1 WITH V_REPLACE( "File corruption possible", fld1)
Correct: <fld1> is being used consistently in DBV area 1.
V_SELECT(1)
REPLACE fld1 WITH V_REPLACE( "Much better", fld1 )
└──────────────────────────────┐
│
REPLACE fld1 WITH V_REPLACE( "Best", fld1 )
┌────────────────────────── WARNING ──────────────────────────┐
│ V_REPLACE() deletes the data associated with <cOldPointer> │
│ before returning <cNewPointer>. Attempting to use │
│ <cOldPointer> after this function deletes the data that it │
│ points to can corrupt the DBV file. │
└─────────────────────────────────────────────────────────────┘
Notes
■ All file sharing within the DBV file is handled automatically.
It is the programmers responsibility to handle any one
pointer-field exactly as you would any DBF field (i.e. use
Clippers FLOCK()/RLOCK() functions). If a record is successfully
locked with Clippers RLOCK() function, the pointer-field will be
inaccessible to other users until the current task releases it.
■ If you use comparison of data in your networking code you are
guaranteed that the pointer stored in the DBF field will be
different after a replace even if the <cNewPointer> points to the
same disk location as <cOldPointer>. It is, therefore, not
necessary to compare the data itself (to see if it has been
changed by one user while another user is working with it), but
rather, it suffices to compare the pointers themselves.
Examples
// Example 1: exclusive
// Assume the DBF file is indexed on the field scr_name
// Assume the DBF file has a pointer-field scr_vlf.
LOCAL cScr, aPickList
// Open a DBF, an NTX and a related DBV file
USE dbf_file INDEX dbf_file
V_USE( "dbv_file" )
// Save a screen segment to a variable.
cScr = SAVESCREEN( 5, 5, 10, 50 )
// Create array of Items on screen.
aPickList = { "1. Add customer" ,;
"2. Edit customer" ,;
"3. Delete customer" ,;
"4. Quit" }
// Make a name for the screen and append the screen and array to
// the dbv_file.dbv.
APPEND BLANK
REPLACE scr_name WITH "main_menu"
REPLACE scr_vlf WITH V_REPLACE( cScr, scr_vlf )
REPLACE picklist WITH V_REPLACE( aPickList, picklist )
// Later or from a different application, restore
// the screen segment and array.
CLEAR
SEEK "main_menu"
RESTSCREEN( 5, 5, 10, 50, V_RETRIEVE( scr_vlf ) )
aPickList = V_RETRIEVE( picklist )
// Example 2: Network example
// Assume the file has a pointer-field memo_vlf
LOCAL ptr, cStr
// Set exclusive off for Clipper and FlexFile.
SET EXCLUSIVE OFF
V_EXCLUSIV( .F. )
// Open the files
USE dbf_file INDEX dbf_file
IF V_USE( "dbv_file" ) == -1
? "File not available at this time."
QUIT
ENDIF
// Make up some data; append it to the files.
APPEND BLANK
WHILE NETERR()
APPEND BLANK
ENDDO
// Lock the record an put some test data in the vlf.
WHILE !RLOCK()
ENDDO
REPLACE dbf_file->memo_vlf WITH V_REPLACE( "Testing",
dbf_file->memo_vlf )
UNLOCK
// Remember the pointer for an "updated" test (see below).
ptr = memo_vlf
// Edit the existing data. Note: the file is not locked.
cStr = MEMOEDIT( V_RETRIEVE( memo_vlf ), 5, 5, 20, 50 )
// Lock the record before the replace.
WHILE !RLOCK()
ENDDO
// Test for an update between when we assigned memo_vlf to
// ptr and now.
IF ptr != dbf_file->memo_vlf
? "This memo field has been updated by another user."
? "It cannot be saved."
ELSE
REPLACE memo_vlf WITH V_REPLACE( cStr, memo_vlf )
ENDIF
UNLOCK
// Close the files.
USE
V_USE()
See Also: V_USE() V_RETRIEVE() V_POKE()
───────────────────────────────────────────────────────────────────────────────
V_RETRIEVE() / V_RET()
Return data that has been stored in a DBV file
───────────────────────────────────────────────────────────────────────────────
Syntax
V_RETRIEVE( <cPointer>,
[<nArea> | <cAlias>],
[<nStart>],
[<nCount>] ) -> Data
Arguments
<cPointer> is a six byte pointer-field (FlexFile's version of a
memo-field).
<nArea> or <cAlias> refers to the number or name of the target DBV
work area. If not specified, the current DBV work area is assumed.
<nStart> is the starting position in <cPointer>'s data. If
<nStart> is positive, it counts from the beginning of the data
pointed to by <cPointer>. If <nStart> is negative, it counts from
the end of the data pointed to by <cPointer>.
<nCount> is how many bytes of data to retrieve. If omitted,
<nCount> begins at <nStart> and goes to the end of the data. If
<nCount> is larger than the data, the excess is ignored.
Returns
V_RETRIEVE() will return the data pointed to by <cPointer>. On
error, V_RETRIEVE() will return an empty value of the type stored
or a logical (.F.) if no type can be determined. Use V_ERROR() to
fetch the error code associated with a failed RETRIEVE().
Description
V_RETRIEVE() retrieves data stored in a DBV type file. The data
retrieved will have the same type as the data that was stored
during the V_REPLACE() (with the notable exception of Clipper
arrays which are discussed below and Proclip windows which use the
V_WNDCREAT() function).
Notes
■ Clipper arrays stored using the V_REPLACE() function cannot be
retrieved with V_RETRIEVE() when using the S87 compiler. Instead, use
V_FILLARR() for Summer 87 type arrays.
■ <nStart> and <nCount> only apply to character data. They will
be ignored for any other data types.
Examples
// Open the DBF file and its associated DBV file
#define CR_LF ( CHR(13) + CHR(10) )
LOCAL cStr
USE dbf_file // Has a 6 byte pointer-field: vlf.
V_USE( "dbv_file" )// Is created if it does not exist.
cStr ="And the silken, sad, uncertain,"+ CR_LF +;
"rustling of each purple curtain,"+ CR_LF +;
"thrilled me, filled me," + CR_LF +;
"with fantastic terrors never felt before."
// Store the string in DBV file and its pointer in
// the DBF.
APPEND BLANK
REPLACE author WITH "Poe"
REPLACE poem WITH "The Raven"
REPLACE vlf WITH V_REPLACE( cStr, vlf )
// Edit the string using V_RETRIEVE().
cStr = MEMOEDIT( V_RETRIEVE( vlf ), 0, 0, 10, 45 )
// Save the edited poem.
REPLACE vlf WITH V_REPLACE( cStr, vlf )
See Also: V_FILLARR() V_REPLACE() V_STUFF() V_LEN()
───────────────────────────────────────────────────────────────────────────────
V_SELECT()
Determine or select the DBV work area of a specified DBV alias
───────────────────────────────────────────────────────────────────────────────
V_SELECT( [<nNewArea>] | [<cAlias>] ) nOldArea
Arguments
<nNewArea> or <cAlias> is the new DBV area (or the new area associated
with <cAlias>).
Returns
V_SELECT() returns the old area if the area is successfully
changed or if no parameters are passed. On error (i.e. an area
beyond the bounds of the V_FILES() setting), V_SELECT() will
return a 0 and the DBV area will remain unchanged.
Description
V_SELECT() provides the functionality of a combination of
Clipper's SELECT command and SELECT() function. Specifically, it
is used to change and/or fetch the active DBV work area.
If no parameters are passed V_SELECT() parallels the Clipper
SELECT() by returning the current DBV area. If, however, a valid
DBV area or alias is passed, V_SELECT() will change the current
DBV area as Clipper's SELECT command would and then returns the
old DBV area as Clipper's SELECT() function would. This
functionality is slightly different than Clipper's SELECT() which
returns the area of the alias but does not actually change the
selected area. If you need to test for the existance of an open
file in a DBV work area, use the V_ISOPEN() function.
V_SELECT() always returns a DBV area number regardless of whether
the parameter passed was an alias or an area.
Notes
■ Do not confuse Clipper's selected work area with FlexFile's;
they are mutually exclusive. For example, you can have a DBF file
open in area 2 and simultaneously have a DBV file open in
V_SELECT() == 2. Likewise, SELECT <n> has no effect on the
currently selected DBV area.
■ Remember that your maximum number of open files must be set by
the V_FILES() function before V_USE()in your first DBV file. The
valid DBV areas will then be from 1 to the value you passed to
V_FILES(). See V_FILES() for more on defining the maximum number
of work areas.
Examples
// Open three DBV files in three new areas.
LOCAL nSelect
nSelect = V_SELECT(1) // Save current area and
// select area 1
V_USE( "file1", "file_one" )// Creates if not existing
V_SELECT(2)
V_USE( "file2" )
V_SELECT(0) // Select the next area
V_USE( "file3" )
? V_ALIAS() // Returns: FILE3
? V_ALIAS(1) // Returns: FILE_ONE
V_SELECT( nSelect ) // Restore the first area.
See Also: V_ALIAS() V_USE() V_ISOPEN() V_FILES()
───────────────────────────────────────────────────────────────────────────────
V_SET_COMMIT()
Set DOS buffer flushing automatically on/off
───────────────────────────────────────────────────────────────────────────────
Syntax
V_SET_COMMIT( [<lToggle>] ) -> lOldSetting
Arguments
<lToggle> is a logical expression which, if true, will turn on
automatic buffer flushing. If false, it will turn off automatic
buffer flushing. If omitted, no change will be made and the
function can be used simply to query the current setting.
Returns
V_SET_COMMIT() returns the old setting of the switch.
Description
DOS "buffers" disk I/O in order to speed up disk access. The
downside to this feature is that the data in the buffers can be
lost if the machine "hangs" or is powered down before the buffers
are copied to disk. This problem was addressed in DOS version 3.3
and later by allowing a call which forces DOS's buffers to be
written to disk. V_SET_COMMIT() sets DOS buffer flushing on
automatic if a logical (.T.) is passed or turns off the automatic
buffer flushing if a logical (.F.) is passed. In the latter case,
use V_COMMIT() to flush buffers manually.
┌──────────────────────── WARNING ─────────────────────────┐
│ V_COMMIT and V_SET_COMMIT() are based on functions │
│ provided in DOS 3.3 and later. Calls made to these │
│ functions on an earlier version of DOS will have no │
│ affect. │
└──────────────────────────────────────────────────────────┘
Example
// Turn off automatic flushing in order to speed up operation.
V_USE( "temp" )
V_SET_COMMIT(.f.)
DO WHILE !EOF()
IF DELETED()
REPLACE vlf_fld WITH V_DELETE( vlf_fld )
ENDIF
SKIP
ENDDO
PACK
// Flush the buffers and turn automatic flushing back on.
V_COMMIT()
V_SET_COMMIT(.T.)
See also: V_COMMIT()
───────────────────────────────────────────────────────────────────────────────
V_SETKEY()
Set a key or password for FlexFile encryption
───────────────────────────────────────────────────────────────────────────────
Syntax
V_SETKEY( <cPassWord> ) -> NIL
Arguments
<cPassWord> is any ten character string. It must be at least one
character and will be truncated to 10 if more characters are supplied.
Returns
NIL
Description
V_SETKEY() affects the return values of V_ENCRYPT() and V_DECRYPT().
Essentially, <cPassWord> is a password that is used to encrypt a string
and must be the same in order to decrypt the string. Once set, the
<cPassWord> is stored statically in memory.
So, for example, you can set a password when your user starts a system
(and enters his/her password) and use that password for the remainder
of the session. All strings V_ENCRYPT()ed with a particular
<cPassWord> can only be V_DECRYPT()ed by using the same <cPassWord>.
Examples
LOCAL cSecure, cOriginal, cJunk
// Set the key to my dog's name.
V_SETKEY( "Tally" )
// Encrypt a character string. Store the result in <cSecure>.
cSecure = V_ENCRYPT( "Just try to decode <cSecure> without my dog." )
// Later, decode the secured string with the help of my golden.
// Note that the <cPassWord> is being changed.
V_SETKEY( "Junk" )
// Because the <cPassWord> has been changed, the resulting string
// will be unreadable.
cJunk = V_DECRYPT( cSecure )
// Now we change the <cPassWord> back to what was used during
// the V_ENCRYPT()ion and the string will be properly restored.
V_SETKEY( "Tally" )
cOriginal = V_DECRYPT( cSecure )
See Also: V_ENCRYPT() V_DECRYPT()
───────────────────────────────────────────────────────────────────────────────
V_STUFF()
Delete and insert new data into an existing VLF
───────────────────────────────────────────────────────────────────────────────
Syntax
V_STUFF( <cOldPointer>,
<nStart>,
<nDelete>,
<cNewString> ) -> cNewPointer
Arguments
<cOldPointer> Is the pointer (usually stored in a DBF field),
which points to the source string into which <cNewString> will be
V_STUFF()ed.
<nStart> is the starting position in the VLF where <cNewString>
will be inserted.
<nDelete> is the number of characters to delete from <cNewString>
starting at <nStart>. Deletions are performed before insertions.
<cNewString> is a string which will be inserted at the <nStart>
position after the deletion of <nDelete> characters.
Returns
V_STUFF() returns a pointer which should be stored in the field
with which the modified variable length data is to be associated.
V_STUFF() returns (.F.) on error. This causes the error system to
be invoked with a "Data Type Mismatch". Use V_ERROR() to fetch
the specific error value (see Appendix B for a table of error
descriptions).
Description
The V_STUFF() function works on VLFs much as Clipper's STUFF()
works on strings. Working on VLFs, V_STUFF() will delete <nDelete>
characters from the string pointed to by <cOldPointer> and then
insert <cNewString> at the <nStart> position. V_STUFF() can,
therefore, perform the following seven functions:
■ Insert: If <nDelete> is zero, V_STUFF() will not delete any
characters before inserting <cNewString> at the <nStart> position.
This effectively makes V_STUFF() an insert function.
■ Replace: If <nDelete> equals LEN( <cNewString> ), then
V_STUFF() will effectively replace the data beginning at the
<nStart> position. If the <cNewString> is always the same length
as <nDelete> or if the comparison for equivalent length is easily
tested, it is faster (less disk I/O) to use V_POKE().
■ Delete: If LEN( <cNewString> ) == 0 or <cNewString> is not
passed, V_STUFF() will effectively delete <nDelete> characters
beginning at the <nStart> position.
■ Replace and Insert: If <cNewString> is longer than <nDelete>,
V_STUFF() will replace <nDelete> characters of the data pointed to
by <cOldPointer> and insert the remaining length of <cNewString>.
■ Replace and Delete: If <cNewString> is shorter than <nDelete>,
V_STUFF() will replace <nDelete> characters of the data pointed to
by <cOldPointer> and then delete the remaining <nDelete>
characters making the data shorter.
■ Replace and Delete rest: If <nDelete> is greater than or equal
to the number of characters remaining after the <nStart> position
of the data pointed to by <cOldPointer>, the old data is
effectively trimmed at the <nStart> position before the
<cNewString> is inserted.
■ Append: If <nStart> is equal to or greater than the length of
the data pointed to by <cOldPointer>, the <nDelete> parameter is
ignored and the <cNewString> data is appended to the end.
┌────────────────────────── WARNING ──────────────────────────┐
│ V_STUFF() deletes the data associated with <cOldPointer> │
│ before returning <cNewPointer>. Attempting to use │
│ <cOldPointer> after this function deletes the data that it │
│ points to can corrupt the DBV file. │
└─────────────────────────────────────────────────────────────┘
Notes
The big advantage to V_STUFF() is that its operation is disk based
rather than memory based. This is, however, a double edged sword.
On the one hand, it offers the ability to create a VLF that is
larger than 65K (Clipper's largest character string size). On the
other hand, if you are not aware that <cNewPointer> points to data
that is larger than 65K, you may be surprised when V_RETRIEVE() or
similar functions trim their return value to 65K. Although
V_RETRIEVE() can only return a maximum length of 65K, it can pick
up any portion of a longer string as long as the portion itself is
no longer than 65K (see V_REPLACE()).
In all cases, V_STUFF() will move the data pointed to by
<cOldPointer> to a new location in the DBV file. It is, therefore,
less disk intensive to use V_POKE() where possible. V_POKE() does
not delete or insert, but simply overwrites data leaving it at the
same disk location within the DBV file.
Examples
// Open a DBF file and its associated DBV file.
USE dbf_file
V_USE( "dbv_file" )
// Put a string away.
cStr = "ABCDEF"
REPLACE vlf WITH V_REPLACE( cStr, vlf )
// Insert.
REPLACE vlf WITH V_STUFF( vlf, 2, 0, 'xyz' )
? V_RETRIEVE( vlf ) // Result: AxyzBCDEF
// Replace.
REPLACE vlf WITH V_STUFF( vlf, 2, 3, 'qrs' )
? V_RETRIEVE( vlf ) // Result: AqrsBCDEF
// Delete.
REPLACE vlf WITH V_STUFF( vlf, 2, 3, '' )
? V_RETRIEVE( vlf ) // Result: ABCDEF
// Replace and Insert.
REPLACE vlf WITH V_STUFF( vlf, 2, 2, 'xyz' )
? V_RETRIEVE( vlf ) // Result: AxyzDEF
// Replace and delete.
REPLACE vlf WITH V_STUFF( vlf, 2, 4, 'qrs' )
? V_RETRIEVE( vlf ) // Result: AqrsEF
// Replace and delete rest.
REPLACE vlf WITH V_STUFF( vlf, 2, 10, 'xyz' )
? V_RETRIEVE( vlf ) // Result: Axyz
// Append.
REPLACE vlf WITH V_STUFF( vlf, 10, 0, 'qrs' )
? V_RETRIEVE( vlf ) // Result: Axyzqrs
See Also: V_POKE() V_REPLACE() V_LEN() V_MEMOLINE()
───────────────────────────────────────────────────────────────────────────────
V_TIMEOUT()
Advanced feature allows setting the internal lock timeout value
───────────────────────────────────────────────────────────────────────────────
Syntax
V_TIMEOUT( [<nTimeOut>] ) -> nOldSetting
Arguments
<nTimeOut> is the time interval (in seconds) before FlexFile will
abort a function that is attempting an internal lock. The default
value is forever.
Returns
V_TIMEOUT() returns the current timeout value.
Description
V_TIMEOUT() should not usually be necessary. If you are using
Clipper's RLOCK() and FLOCK() functions to manage the DBF file,
and you are keeping FlexFile's pointers in the DBF (this is the
preferred method), then this function is not necessary.
If you want FlexFile to timeout during an operation, then use this
function to set the timeout value in seconds. Passing zero as
<nTimeOut> will set the timeout value back to its default
(forever).
If you set the timeout to something other than zero, you must test
the error code following a call to any of the FlexFile functions
in the table below. The test is made by comparing V_ERROR() to
zero or 5200. If the operation's lock timed out, V_ERROR() will
return 5200, if the operation was successful, V_ERROR() will
return zero. (See the example below.)
Functions affected by V_TIMEOUT(): V_DELETE(), V_FILE2DBV(),
V_REPLACE(), V_STUFF(), V_DBV2FILE()
Examples
// Setup defines and variables
#define EXCLUSIVE_ (.T.)
LOCAL error_no
// Set the timeout to 3 seconds.
V_TIMEOUT(3)
// Open a DBF file and its related DBV file.
USE dbf_file
IF V_USE( "dbv_file", , , !EXCLUSIVE_ ) == -1
error_no = V_ERROR()
IF error_no == 5200
? "Attempt to lock header failed after 3 seconds"
RETURN .F.
ELSE
? "Error: " + str( error_no, 4 )
RETURN .F.
ENDIF
ENDIF
See Also: V_USE() V_REPLACE()
───────────────────────────────────────────────────────────────────────────────
V_TOPPTR()
Read/Replace FlexFile data without a DBF file.
───────────────────────────────────────────────────────────────────────────────
Syntax
V_TOPPTR( [<cPointer>], [<cAlias> | <nArea>] )
Arguments
<cPointer> (optional) is the six byte character string returned by
V_REPLACE(). If not specified, V_TOPPTR() will return the current
pointer stored in the header of the DBV file (see discussion below).
<cAlias> or <nArea> is a DBV file alias or area specified with
V_USE() or V_SELECT(). If omitted, the Top-Pointer will be accessed in
the current DBV area.
Description
V_TOPPTR() is a special function which allows the storage of one (and
only one) Variable Length Field without having a separate DBF file in
which to keep the pointer to that field.
Usually, you replace a six byte character field (FlexFile's version of
a memo-field) in a DBF file with a pointer to data which is stored in a
DBV file. V_TOPPTR() allows you to store one pointer in the header of
the DBV file. This pointer can then be accessed by calling V_TOPPTR()
with no parameters. For example,
V_RETRIEVE( V_TOPPTR() )
would retrieve an array which was previously stored with V_REPLACE()
and whose pointer was stored by V_TOPPTR().
A good example of the use of this function is in storing parameters
that your application requires at startup: Things like colors, default
paths, printer definitions, etc.
Many routines have been written to handle this kind of data. MEM files
are used by some, while others use a large DBF field and parse out the
various data into variables. The fact is that MEM files are not
"network ready" and the DBF file is not efficient to handle data that
is not repetitive in size and type. So an easy solution is to keep an
array of these items in a DBV file and point to that array with
V_TOPPTR().
Using V_TOPPTR() requires a slightly different syntax than a standard
replace. Typically, a DBV replace is made by calling V_REPLACE() and
putting the return pointer in a DBF field as follows:
REPLACE vlf WITH V_REPLACE( aStartUp, vlf )
where aStartUp is an array holding your applications startup data. In
contrast, the following syntax is required when storing the
pointer-field directly in the DBV:
V_TOPPTR( V_REPLACE( aStartUp, V_TOPPTR() ) )
The logic here can best be seen by breaking this last line into several
lines of code:
// First, get the old pointer stored in the DBV file.
cOldPointer = V_TOPPTR()
// Then replace the old array with a new one. Put
// the pointer to the new array into the variable
// cNewPointer.
cNewPointer = V_REPLACE( aStartUp, cOldPointer )
// Finally, store the actual pointer in the DBV.
// This will overwrite the old pointer stored there.
V_TOPPTR( cNewPointer )
Examples
LOCAL ray
// Open only a DBV file
V_USE( "dbv_file" ) // Is created if it does not exist.
// Declare an array and put some junk into it.
ray = { { "one", "two", "three" } ,;
{ 1, 2, 3 ) ,;
{ p1, p2, p3 } }
// Store the array in DBV file and its pointer in
// the header of the same DBV.
V_TOPPTR( V_REPLACE( ray, V_TOPPTR() ) )
See Also: V_REPLACE() V_RETRIEVE()
───────────────────────────────────────────────────────────────────────────────
V_TYPE()
Return the type of data stored in a VLF
───────────────────────────────────────────────────────────────────────────────
Syntax
V_TYPE( <cPointer>,
<nArea> | <cAlias> ) -> cType
Arguments
<cPointer> is a six byte pointer-field (FlexFile's version of a
memo-field).
<nArea> or <cAlias> refers to the number or name of the target DBV
work area. If not specified, the current DBV work area is assumed.
Returns
V_TYPE() returns the type of the data that is pointed to by
<cPointer>. The possible types and their one character pneumonic
are detailed in the table under the V_REPLACE() function.
Description
V_TYPE() is similar to Clipper's TYPE() function. There are,
however, more "types" of data that can be stored to a VLF than in
a Clipper memory variable. For example, Clipper see's a Proclip
window handle as a numeric. Clipper's TYPE("pw_handle") function
would return a 'N'. However, if you save the Proclip window to a
VLF (See V_REPLACE()), the V_TYPE() function will return a 'P'.
Examples
// Setup seven variables of differing types.
LOCAL num_var ,;
chr_var ,;
date_var ,;
log_var ,;
arr_var ,;
ffa_var ,;
pcw_var
num_var = 5
chr_var = "This is a test"
date_var= DATE()
log_var = (.T.)
arr_var = { "Cindy", "Gail", "Edith" }
ffa_var = A_DECLARE( 'S', 5, 5 )// FlexFile double type
pcw_var = WNDCREATE( 5, 10 ) // Proclip window.
// Open a DBF file and its associated DBV file
USE dbf_file
V_USE( "dbv_file" )
// Replace the VLF with five different data types.
APPEND BLANK
REPLACE vlf WITH V_REPLACE( num_var, vlf )
? V_TYPE( vlf ) // Result: 'N'
REPLACE vlf WITH V_REPLACE( chr_var, vlf )
? V_TYPE( vlf ) // Result: 'C'
REPLACE vlf WITH V_REPLACE( date_var, vlf )
? V_TYPE( vlf ) // Result: 'D'
REPLACE vlf WITH V_REPLACE( log_var, vlf )
? V_TYPE( vlf ) // Result: 'L'
REPLACE vlf WITH V_REPLACE( arr_var, vlf )
? V_TYPE( vlf ) // Result: 'A'
// Note: You must declare the type for a V_REPLACE()
// of a flexfile strongly typed array.
REPLACE vlf WITH V_REPLACE( ffa_var, vlf, , 'F' )
? V_TYPE( vlf ) // Result: 'F'
// Note: You must declare the type for a V_REPLACE()
// of a Proclip window handle.
REPLACE vlf WITH V_REPLACE( pcw_var, vlf, , 'P' )
? V_TYPE( vlf ) // Result: 'P'
See Also: V_REPLACE() V_RETRIEVE() V_LEN()
───────────────────────────────────────────────────────────────────────────────
V_USE()
Open/Create a Variable Length Field file (DBV)
───────────────────────────────────────────────────────────────────────────────
Syntax
V_USE( [<cFileName>],
[<cAlias>],
[<cNewArea>],
[<lExclusive>] ) -> nArea
Arguments
<cFileName> is the name of the file to be opened/created. A .DBV
extension is assumed if none is provided. If the file does not
exist, one will be created. If <cFileName> is omitted, any file
open in the current DBV area will be closed.
<cAlias> is a name to be associated with the DBV work area of the
file being opened. If this parameter is not passed or a "dummy"
argument is passed, the alias defaults to the DBV file name.
<cNewArea> is the word "NEW". This has the same affect as issuing
a SELECT 0 in Clipper or V_SELECT(0) in FlexFile just before
opening the file. In Clipper version 5.0, <cNewArea> has the same
effect as the NEW modifier of the USE command. If this parameter
is not specified, any DBV file open in the current DBV work area
will be closed and the <cFileName> file will be opened in its
place.
<lExclusive> is a logical expression determining the accessibility
of the file by other users in a network environment. If
<lExclusive> is (.T.) and the file is successfully opened, any
attempt by another user to V_USE() the <cFileName> file will be
denied until the file is V_CLOSED(). If this parameter is omitted,
FlexFile will attempt to open the <cFileName> file according to
the current setting of V_EXCLUSIV().
Returns
If successful, V_USE() returns the DBV area in which the file was
opened. Otherwise, it will return a -1 and set an error code which
may be fetched with V_ERROR(). (See appendix B for a list of error
codes.)
Description
V_USE() opens (or creates if the file does not exist) a file that
can store variable length data. Any file which is already open in
the DBV area is closed before the <cFileName> file is opened.
Although it is far more flexible, using a DBV file is very similar
to using memo-fields and a DBT file. It is not required but highly
recommended that you have a DBF file open simultaneously with the
DBV file. You will then store pointers in fields of the DBF file
that point to variable length data in the DBV file. The fields in
the DBF file cannot be defined as a <memo> fields but have the
same basic functionality.
Unlike the DBT file, FlexFile supports one to one, one to many,
and many to many relationships between the DBF field pointers and
the DBV files. For example, a system could use one DBV file to
store all its help text, random memo's, screens and arrays. The
pointers to all this data could be stored in several different DBF
files. Likewise, one DBF file may point into several DBV files. It
is left up to the programmer to keep straight which DBF fields
relate to which DBV files. (Note: it is a critical error to use a
pointer which points into one DBV file as a pointer into a
different DBV file.)
If a DBV file is opened exclusively in a network environment, no
other users may access the file until it is V_CLOSED(). Likewise,
if the file is currently being used exclusively by another user,
attempts to V_USE() that file will fail. V_USE() returns -1 if
the file is not opened and reports the error which can be fetched
with V_ERROR(). (See appendix B for a list of error codes.)
If the file is opened non-exclusively, other users may access the
variable length data simultaneously. However, because all data in
a DBV file must be accessed by a pointer which will usually be
stored in a DBF file, using standard Clipper locking procedures to
protect the DBF record will protect the variable length data
exactly as it does any field in the DBF. (See the V_REPLACE()
function for more on locking.)
Notes
■ V_USE() obeys neither the SET DEFAULT nor the SET PATH
settings. Instead, it will use the current DOS default directory
or any valid path specified as a part of <cFileName>.
■ Remember that your maximum number of open files must be set by
the V_FILES() function before V_USE()ing your first DBV file. The
valid DBV areas will then be from 1 to the value you passed to
V_FILES(). See V_FILES() for more on defining the active number of
files.
■ Opening the same DBV file in two work areas simultaneously is
possible, however, don't do it.
■ Do not confuse Clipper's selected work area with FlexFile's;
they are mutually exclusive. For example, you can have a DBF file
open in area 2 and simultaneously have a DBV file open in
V_SELECT() == 2. Likewise, Clipper's SELECT <n> has no effect on
the currently selected DBV area.
Examples
// Assume that no files are open at this point.
// Set the maximum number of files to three.
#define EXCLUSIVE_ (.T.)
V_FILES( 3 )
// Open a DBV file with an alias of "MAIN_FILE" in area 1.
// If dbv_file.dbv does not exist it will be created.
V_SELECT(1)
V_USE( "dbv_file", "MAIN_FILE" )
? V_ALIAS() // Result: MAIN_FILE
// Open another file in the next available area.
V_USE( "second", , "NEW" )
? V_SELECT() // Result: 2
// Open a third file for use on a network and test to
// make certain that the file was properly opened.
IF V_USE( "F:\GEN\JUNK\third", , "new", !EXCLUSIVE_ ) == -1
? "Error: " + str( V_ERROR(), 4 )
QUIT
ENDIF
// Close all files.
V_CLOSE( 1 ) // Closes file in area 1
V_CLOSEALL() // Closes the other two files.
See Also: V_CLOSE() V_SELECT()
───────────────────────────────────────────────────────────────────────────────
V_VLF2FILE()
Create a DOS file and fill it with the contents of a VLF
───────────────────────────────────────────────────────────────────────────────
Syntax
V_VLF2FILE( <cFileName>,
<cPointer>,
<cMode> ) -> lSuccess
Arguments
<cFileName> is the name of a file to append to or overwrite with
the data from a VLF associated with <cPointer>.
<cPointer> is a six byte pointer-field (FlexFile's version of a
memo-field).
<cMode> is a character code telling FlexFile to <A>ppend or
<O>verwrite any existing file with the same name as <cFileName>.
The default mode is <A>ppend.
Returns
V_VLF2FILE() returns (.T.) if the data was copied successfully,
otherwise, it returns (.F.).
Description
V_VLF2FILE() copies a Variable Length Field from a DBV type file
to a DOS file. This is particularly useful when using graphics
applications from within Clipper that require individual files for
images.
Examples
// Setup variable
LOCAL pic_name
// Open a DBF, NTX and DBV library of graphics images.
USE ( pic_lib ) INDEX ( pic_lib )
V_USE( pic_lib )
// Allow the user to enter the name of a graphic image
// to view, create a temporary disk file, print the
// file to screen.
WHILE ( .T. )
pic_name = SPACE(11)
@ 5, 5 SAY "Enter the image name to view: " GET pic_name
SEEK pic_name
IF FOUND()
// Write DBV data to a temporary file.
V_VLF2FILE( "graphic.pic", vlf_ptr )
// Use one of the graphic function libraries to retrieve a file.
file2graph( 0, "graphic.pic" )
INKEY(0)
ELSEIF LASTKEY() == 27
EXIT
ELSE
? "Image not found"
ENDIF
ENDDO
See Also: V_FILE2VLF() V_REPLACE() V_DELETE()
───────────────────────────────────────────────────────────────────────────────
V_WALK_DBV()
Step from field to field in a DBV without a DBF.
───────────────────────────────────────────────────────────────────────────────
Syntax
V_WALK_DBV( [<cTopOfFile>], [<cAlias> | <nArea>] ) -> cPointer
Arguments
<cTopOfFile> should be the word "First" (only the "F" is significant)
to return the pointer to the first field in a DBV. Any other value
(including NIL) will tell V_WALK_DBV() to skip to the next field.
<cAlias> or <nArea> is a DBV file alias or area specified with
V_USE() or V_SELECT(). If omitted, the operation will occur in the
current DBV area.
Returns
This function returns a pointer to the current VLF. (See discussion
below). The return value is a NULL string ("") when the end of file is
reached or when an error is encountered in the DBV.
Description
V_WALK_DBF() is a disaster recovery function and should rarely if every
be needed. However, if you loose a DBF file and still want to recover
the contents of the DBV, this function can be indispensible.
When called for the first time or when <cTopOfFile> is given the
value "first", V_WALK_DBV() will return a pointer to the first Variable
Length Field in the DBV (this is the first physical field). Any
subsequent call to this function (where <cTopOfFile> is not
"first"), V_WALK_DBV() will return a pointer to the next physical field
in the DBV.
Thus, when put in a loop, you can walk through every field in a DBV and
retrieve a valid FlexFile pointer for use with V_RETRIEVE(),
V_REPLACE(), etc.
Examples
LOCAL vlf_ptr, str := ""
// Open the DBV without opening a DBF.
V_USE( "lost_dbf.dbv" )
// Put the pointer to the first field in the variable <vlf_ptr>.
// The "First" is not necessary here because the first call to this
// function starts on the first field anyway, but is included
// for clarity.
vlf_ptr = V_WALK_DBV( "First" )
DO WHILE !EMPTY( vlf_ptr )
str = V_RETRIEVE( vlf_ptr )
MEMOEDIT( str, 5, 5, 20, 75 )
// Get a pointer to the next field (fields are in physical order)
vlf_ptr = V_WALK_DBV()
ENDDO
See Also: V_RETRIEVE()
───────────────────────────────────────────────────────────────────────────────
V_WNDCREAT()
Create a Proclip(tm) window from one which was saved to a DBV file.
───────────────────────────────────────────────────────────────────────────────
Syntax
V_WNDCREAT( <cPointer>, [<cAlias> | <nArea>] ) -> nPCWhandle
Arguments
<cPointer> is a six byte pointer-field (FlexFile's version of a
memo-field).
<cAlias> or <nArea> is a DBV file alias or area specified with
V_USE() or V_SELECT(). If omitted, the operation will occur in the
current DBV area.
Returns
If V_WNDCREAT() is successful, it will return a valid Proclip
window "handle". Otherwise, it will return 0.
Description
V_WNDCREAT() will re-create a Proclip window that was previously
stored in a DBV file with the V_REPLACE() function. The window
will have all the attributes of the original window with the single
exception that it will not be opened even if it was open when it
was saved.
In order to open or modify the window, you will need the Proclip
library from Genesis Development Corporation. It is one of the
finest windowing libraries available.
Examples
// Create a Proclip window using the Proclip library.
handle = WNDCREATE( 10, 20 )
// Put some junk on the window.
FOR x = 1 TO 10
WNDPRINT( handle, x - 1, 0, "this is a test" )
NEXT x
// Show the user the window.
WNDOPEN( handle )
// Save the window to the DBV file open in the current area.
// Note the necessity to "type" the replace (i.e. the 4th parameter).
REPLACE vlf WITH V_REPLACE( handle, vlf, .t., 'P' )
// Close and then destroy the window.
WNDCLOSE( vlf )
WNDDESTROY( vlf )
// Now re-create the same window.
handle = V_WNDCREAT( vlf )
// Re-display the window
WNDSHOW( handle )
See Also: V_FILE2VLF() V_REPLACE() V_DELETE()
───────────────────────────────────────────────────────────────────────────────
A_()
Return a pointer reference to a multi-dimensional array element
───────────────────────────────────────────────────────────────────────────────
Syntax
A_( <nElements>,
[, <nElements>...] ) -> nPointer
Arguments
<nElements> represents the magnitude of each successive dimension
in a FlexFile array.
Returns
A_() returns a character string which FlexFile interprets as a
pointer to the specific element.
Description
Use A_() to simplify passing/receiving multi-dimensional array
index references. The need for the function arises because array
indexes have as many components as the array has dimensions and
the passing of the index to a function can get confusing (both for
the programmer and FlexFile's error checking).
With Clipper arrays the compiler handles references to a
particular element with the familiar syntax [ 1 ][ 1 ][ 1 ] for
the first element of a three dimensional array. Avoiding conflict
with this syntax, FlexFile refers to the same element of a
FlexFile three dimensional array as A_( 1, 1, 1 ).
Although FlexFile functions will accept a numeric integer as an
array index designator (it is the preferred method on a FlexFile
single dimensional array), you should not use integers as indexes
to multi- dimensional arrays. This is because A_( 3, 5 ) does not
refer to the same element as A_(5, 3), and rarely if ever does
(3 * 5) point to element A_(3, 5).
See the examples for a complete understanding of the use of this
function.
Examples
// Accessing elements of a FlexFile Multi-dimensional array
// First declare a two dimensional 5 x 10 array.
rows = 5
cols = 10
aName = A_DECLARE( 'F', rows, cols )
// Fill the array starting at the first element a_( 1, 1 ).
A_FILL( aName, 98.6, A_( 1, 1 ), rows * cols )
// Retrieve a value from a specified element.
temp = A_RETRIEVE( aName, A_( 3, 2 ) )
See Also: A_RETRIEVE() A_STORE()
───────────────────────────────────────────────────────────────────────────────
A_ADD()
Add a new element to the end of an array
───────────────────────────────────────────────────────────────────────────────
Syntax
A_ADD( @<aTarget>,
[<nDimension>] ) -> lSuccess
Arguments
<aTarget> is the array to which you are adding new element(s).
It MUST be passed-by-reference. A_ADD() cannot add elements to
a binary (logical) type array.
<nDimension> is the dimension that will be increased by one.
The default is the last dimension in the list of dimensions.
Returns
A_ADD() returns a (.T.) if successful, otherwise, it returns a
(.F.).
Description
A_ADD() works differently than Clipper's AADD() function. This is
because FlexFile arrays are matrices and cannot have "ragged"
lengths in any dimension.
So, A_ADD() adds one element in a one dimensional array, one row
in a two dimensional array, one page in a three dimensional array,
and so forth.
For example, imagine a two dimensional array containing students
grades. Along one axis are the students; along the other are the
exams. When the semester begins the professor may not know how
many exams there are going to be (or how to handle a new student).
If you look at both of these situations you will understand
FlexFile's A_ADD().
Lets assume that the professor begins with 20 students and plans
the course to consist of 5 exams:
students = 20
exams = 5
array = A_DECLARE( 'I', students, exams )
Later in the course he needs to add a new student.
A_ADD( @array, 1 )
The second parameter is one to tell A_ADD that we are adding a
row of exams for a new student. The value is one because when
we declared the array the students were the first array
index.
Later the professor needs to add another exam for all students:
A_ADD( @array, 2 )
Examples
// Declare a two dimensional unsigned (T)iny integer array
LOCAL rows, cols
rows = 10
cols = 20
aTiny = A_DECLARE( "UT", rows, cols )
// Increase the columns to 21.
A_ADD( @aTiny, 2 )
See Also: A_SIZE() A_DECLARE()
───────────────────────────────────────────────────────────────────────────────
A_AVERAGE()
Return the average of the contents of a numerical array
───────────────────────────────────────────────────────────────────────────────
Syntax
A_AVERAGE( <aTarget>,
[<idxPos>],
[<nCount>] ) -> nAverage
Arguments
<aTarget> is the FlexFile array on which to perform the average.
<idxPos> The index (or subscript) is the position in the target
array at which to begin the average. The default is the first
element.
<nCount> is the number of elements to include in the average. The
default is all element starting at <idxPos> to the end of the
array.
Returns
A_AVERAGE() returns the numerical average or mean of the <aTarget>
array. On error, A_AVERAGE() returns 0.
Description
A_AVERAGE() will average all or a part of a FlexFile numeric
array. The numeric types are (D)ouble, (F)loat, (L)ong or (UL),
(I)nteger or (UI), and (T)iny or (UT).
The averaging begins at the position pointed to by <idxPos> and
continues for <nCount> elements. The function will wrap around
dimensions if count extends beyond the end of the current row.
Examples
// Declare a two dimensional (L)ong integer array
LOCAL rows, cols
rows = 10
cols = 20
aLong = A_DECLARE( "L", rows, cols )
// Fill the 5th row with 100,000 and the 6th row
// with 200,000.
A_FILL( aLong, 100000, A_( 5, 1 ), cols )
A_FILL( aLong, 200000, A_( 6, 1 ), cols )
// Take the average of rows 5 and six.
? A_AVERAGE( aLong, A_( 5, 1 ), cols * 2 ) // Result: 150000
See Also: A_STORE() A_RETRIEVE()
───────────────────────────────────────────────────────────────────────────────
A_COPY()
Copy all or a portion of one array into another existing array
───────────────────────────────────────────────────────────────────────────────
Syntax
A_COPY( <aSource>,
<aTarget>,
[<idxPos>],
[<nCount>],
[<nTargetPos>] )
Arguments
<aSource> is the array that will be copied from.
<aTarget> is the array that will be copied to.
<idxPos> is the element in <aSource> where the copy will begin. The
default is the first element in the array.
<nCount> is the number of elements to copy. The default is all
elements starting at <idxPos> until the end of the row in the
current dimension.
<nTargetPos> is the starting element position in the target array.
Returns
None.
Description
A_COPY() copies the contents of one array into another. This is
different than Clipper's ACOPY() which copies references to nested
array elements. Because there are no nested array elements in a
FlexFile array, the copy includes the smaller of the range
specified and the size of the arrays.
Examples
// Declare a two dimensional (UL)ong integer array
LOCAL rows, cols
rows = 10
cols = 20
aSource = A_DECLARE( "UL", rows, cols )
aTarget = A_DECLARE( "UL", rows, cols )
// Fill the 5th row with 100,000 and the 6th row
// with 200,000.
A_FILL( aSource, 100000, A_(5, 1), cols )
A_FILL( aTarget, 200000, A_(6, 1), cols )
// Copy the 5th and 6th rows to the 2nd/3rd row of the target
A_COPY( aSource, aTarget, A_(5, 2), cols * 2, A_(2, 1) )
See Also: A_SIZE() A_INS()
───────────────────────────────────────────────────────────────────────────────
A_DECLARE()
Declare a FlexFile array
───────────────────────────────────────────────────────────────────────────────
Syntax
A_DECLARE( <cType>,
<nElement>,
[<nElements>...] ) -> aArray
Arguments
<cType> is a code for the type of the FlexFile strongly typed
array. (See the table below for a list of types and their codes.)
<nElements> is the number of elements in each consecutive
dimension. An array can be declared with up to six dimensions.
Returns
A_DECLARE() returns a fully dimensioned FlexFile array of <cType>.
Description
A_DECLARE() creates a FlexFile type array. The various data types
that FlexFile implements are listed below.
When Clipper returns a value from a function, it makes a copy of
the value for the calling function before releasing the value
created by the called function. So, if you declare a FlexFile
character array of 60,000 elements, A_DECLARE() will allocate
memory for an array of 60,000 character elements (a little over
60,000 bytes) and then Clipper will copy that variable requiring
two 60,000 byte blocks to execute the function.
Although this is not a problem with smaller arrays, it becomes
significant with larger ones. FlexFile addresses this problem by
offering an optional syntax for this function. Just add a new
parameter and insert it after the type. This parameter must be a
previously declared character string of any length and it must be
passed-by-reference. The optional syntax is as follows:
A_DECLARE( <cType>,
@<aLargeArr>,
<nElement>,
[<nElements>...] )
The return value when this syntax is used is undefined. Instead,
FlexFile will modify the <aLargeArr> variable. Because there is
no return value, Clipper will not duplicate the <aLargeArr>
variable.
Table: Data types
┌──────────────────────────────────────────────────────────────────────┐
│ Array Type Type Elem/Arr Byte/Elem Data Range │
├──────────────────────────────────────────────────────────────────────┤
│ Binary (logical) B 520K 1 bit .T. or .F. │
│ Character C 65K 1 byte Extended ASCII set │
│ Tiny Integer T 65K 1 byte -128 to 127 │
│ Unsigned Tiny UT 65K 1 byte 0 to 255 │
│ Integer I 32K 2 bytes -32,768 to 32,767 │
│ Unsigned Integer UI 32K 2 bytes 0 to 65,535 │
│ Long Integer L 16K 4 bytes -2147483648 to 2147483647 │
│ Unsigned Long UL 16K 4 bytes 0 to 4,294,967,295 │
│ Floating Point F 16K 4 bytes 7 digit precision │
│ String S 16K 4 bytes n/a │
│ Double D 8K 8 bytes 15 digit precision │
└──────────────────────────────────────────────────────────────────────┘
Examples
// Declare a two dimensional (B)inary array
LOCAL rows, cols
rows = 10000
cols = 20
aBinary = A_DECLARE( "B", rows, cols )
// Optional syntax. Note: A_DECLARE() has no return value.
LOCAL aLargeArr := ''
A_DECLARE( "UL", aLargeArr, 10000, 2 )
See Also: A_FILL() A_TYPE()
───────────────────────────────────────────────────────────────────────────────
A_DEL()
Delete one element from an array
───────────────────────────────────────────────────────────────────────────────
Syntax
A_DEL( <aTarget>, [<idxPos>], [<nCount>] )
Arguments
<aTarget> is the array from which an element will be deleted.
<idxPos> is the position in the target array of the element to be
deleted.
<nCount> is the number of elements to shift. The default is all
elements starting at <idxPos> until the end of the row in the
current dimension.
Returns
None.
Description
A_DEL() is similar to Clipper's ADEL() function. The array element
pointed to by <idxPos> is removed and <nCount> elements are
shifted up one position. A blank element is inserted at the
position created by the shift and filled with the default value
based on the type of the array (see A_DECLARE()).
FlexFile's multi-dimensional arrays are not nested single
dimensional arrays. Therefore, A_DEL()eting an element has no
other affect than shifting <nCount> elements up one position.
Wrapping will occur if <nCount> goes beyond the current row
pointed to by <idxPos>. For example, if you have a two dimensional
array with accounts in the first dimension and 12 prior period
amounts in the second dimension, and you wanted to operate on the
first quarter (i.e. 3 periods), making <nCount> three times the
number of accounts will "wrap" around three periods. See the
discussion on arrays in chapter 2 for more information on this
feature.
Examples
// Declare a two dimensional (C)haracter array
LOCAL rows, cols, x, y, z
rows = 10
cols = 20
z = 1
aChar = A_DECLARE( "C", rows, cols )
// Fill the array with increasing character values.
FOR x = 1 to rows
FOR y = 1 to cols
z++
A_STORE( aChar, chr(z), A_( x, y ) )
NEXT
NEXT
// Delete the ASCII character "" from the first element
// and shift all elements from the second element to the
// end of the array up one position. Wrapping will occur.
A_DEL( aChar, A_(1, 1), 1 )
See Also: A_DECLARE() A_INS() A_SCROLL()
───────────────────────────────────────────────────────────────────────────────
A_DIMS()
Return the number of dimensions in an array
───────────────────────────────────────────────────────────────────────────────
Syntax
A_DIMS( <aTarget> ) -> nDimensions
Arguments
<aTarget> is the array whose dimensions are being queried.
Returns
A_DIMS() returns the number of dimensions of <aTarget>.
Description
A_DIMS() is used to query an array returning the number of
dimensions that were used when the array was A_DECLARE()d.
Examples
// Declare a four dimensional (S)tring array
LOCAL books, pages, rows, cols
books = 2
pages = 10
rows = 15
cols = 50
aString = A_DECLARE( "S", books, pages, rows, cols )
? A_DIMS( aString ) // Result: 4
See Also: A_DECLARE() A_LEN()
───────────────────────────────────────────────────────────────────────────────
A_DIVIDE()
Divide all elements of an array by a value or an array of values
───────────────────────────────────────────────────────────────────────────────
Syntax
A_DIVIDE( <aTarget>,
<nDenominator> | <aDenominators>,
[<aSource>] )
Arguments
<aTarget> is the array to be modified. If <aSource> is not
specified then each element of <aTarget> is the numerator of the
division.
<nDenominator> or <aDenominators> is either a value or an array of
values used as the denominator in the division.
<aSource> is an optional array of numerators. If not specified,
then each element of <aTarget> will be used as the numerator of
the division.
Returns
Although there is no return value, the <aTarget> array is
modified.
Description
A_DIVIDE() provides a means to perform a division of every element
in the <aSource> array if specified or the <aTarget> array if not.
Each element of the <aSource> or <aTarget> array is divided by the
<nDenominator> or element for element by <aDenominator>. The
result of the division replaces the value in the <aTarget> array.
A_DIVIDE() can be performed on any integer or floating point type
array. These include the (T)iny, (I)integer, (L)ong, (F)loat and
(D)ouble (both signed and unsigned integers, of course). Mixing
of the various numeric types is permitted, however, the source
values are promoted or demoted to the targets type before the
operation is executed.
Division by zero is trapped and the element in error is replaced
with zero.
Notes
■ Division by zero is trapped and the element in error is
replaced with zero.
Examples
// Declare three double type arrays for this function.
LOCAL rows, cols
rows = 10
cols = 20
aSource = A_DECLARE( "D", rows, cols )
aTarget = A_DECLARE( "D", rows, cols )
aDenominator = A_DECLARE( "D", rows, cols )
// Fill the target array with 10's
A_FILL( aTarget, 10, A_(1, 1), rows * cols )
// Divide every element in the target array by 2
A_DIVIDE( aTarget, 2 )
? A_RETRIEVE( aTarget, A_( 4, 5 ) ) // Result: 5.0
// Fill the source array with 10's
A_FILL( aSource, 10, A_(1, 1), rows * cols )
// Divide every element in the source array by five and
// put the result in the target array.
A_DIVIDE( aTarget, 5, aSource )
? A_RETRIEVE( aTarget, A_( 4, 5 ) ) // Result: 2.0
// Fill the denominator array with 4's
// Remember the source array still has all 10's
A_FILL( aDenominator, 4, A_(1, 1), rows * cols )
// Divide every element in the source array by every element
// in the denominator array and put the result in the target
// array.
A_DIVIDE( aTarget, aDenominator, aSource )
? A_RETRIEVE( aTarget, A_( 4, 5 ) ) // Result: 2.5
See Also: A_MULTIPLY() A_PLUS() A_MINUS()
───────────────────────────────────────────────────────────────────────────────
A_ERROR()
Report specific error codes from FlexFile's error system
───────────────────────────────────────────────────────────────────────────────
Syntax
A_ERROR() -> nErrorValue
Arguments
None.
Returns
A_ERROR() returns a FlexFile error code as a numeric integer.
Description
A_ERROR() is an error function that will return the most recent
error code that FlexFile set. If the failed operation has no
FlexFile error code check the DOS error code using Clipper's
DOSERROR().
For a complete list of error codes and their descriptions
refer to Appendix B.
Examples
// Declare a two dimensional array
LOCAL aLong
aLong = A_DECLARE( 'L', 5, 7 )
// Using A_KILL() on a non-string type array is an error.
A_KILL( aLong )
// Show the error code for a type mismatch.
? A_ERROR() // Result: 2001
See Also: errcodes.ngo:"Error Codes"
───────────────────────────────────────────────────────────────────────────────
A_FILL()
Fill all or a portion of an array with a specified value
───────────────────────────────────────────────────────────────────────────────
Syntax
A_FILL( <aTarget>,
<expValue>,
[<idxPos>],
[<nCount>] )
Arguments
<aTarget> is the array which will be filled.
<expValue> is an expression which must evaluate to the same type
as that of the array. This value will be placed in each element of
the fill range.
<idxPos> is the first position to in the <aTarget> array to fill.
If not specified, the default value is the first element in the array.
<nCount> is the number of elements to fill starting at <idxPos>.
If not specified, elements are filled to the end of the array.
Returns
None.
Description
A_FILL() replaces <nCount> elements in the range beginning at
<idxPos> with <expValue>. The value must be of the same type as
the array.
Wrapping will occur if <nCount> goes beyond the current row
pointed to by <idxPos>. For example, if you have a two dimensional
array with accounts in the first dimension and 12 prior period
amounts in the second dimension, and you wanted to operate on the
first quarter (i.e. 3 periods), making <nCount> three times the
number of accounts will "wrap" around three periods. See the
discussion on arrays in chapter 2 for more information on this
feature.
Examples
// Declare a two dimensional (F)loating point array
LOCAL rows, cols
rows = 10
cols = 20
aFloat = A_DECLARE( "F", rows, cols )
// Fill the second row of the array with 98.6
A_FILL( aFloat, 98.6, A_(2, 1), cols )
See Also: A_STORE() A_ADD()
───────────────────────────────────────────────────────────────────────────────
A_INS()
Insert an element into an array
───────────────────────────────────────────────────────────────────────────────
Syntax
A_INS( <aTarget>, [<idxPos>], [<nCount>] )
Arguments
<aTarget> is the array into which an element will be inserted.
<idxPos> is the position in the target array of the element to be
inserted.
<nCount> is the number of elements to shift. The default is all
elements starting at <idxPos> until the end of the row in the
current dimension.
Returns
None.
Description
A_INS() is similar to Clipper's AINS() function. All elements from
<idxPos> for <nCount> will be shifted down one element. The last
element in <nCount> will be pushed into oblivion. A blank element
is inserted at the position created by the shift and filled with
the default value based on the type of the array (see
A_DECLARE()).
FlexFile's multi-dimensional arrays are not nested single
dimensional arrays. Therefore, A_INS()erting an element has no
other affect than shifting <nCount> elements down one position.
Wrapping will occur if <nCount> goes beyond the current row
pointed to by <idxPos>. For example, if you have a two dimensional
array with accounts in the first dimension and 12 prior period
amounts in the second dimension, and you wanted to operate on the
first quarter (i.e. 3 periods), making <nCount> three times the
number of accounts will "wrap" around three periods. See the
discussion on arrays in chapter 2 for more information on this
feature.
Examples
// Declare a two dimensional (C)haracter array
LOCAL rows, cols, x, y, z
rows = 10
cols = 20
z = 1
aChar = A_DECLARE( "C", rows, cols )
// Fill the array with increasing character values.
FOR x = 1 to rows
FOR y = 1 to cols
z++
A_STORE( aChar, chr(z), A_( x, y ) )
NEXT
NEXT
// Insert four blank elements (chr(0)'s) at the first element
// and shift all elements from the second element to the
// end of the array down four positions. Wrapping will occur.
A_INS( aChar, A_(1, 1), 4 )
See Also: A_DECLARE() A_DEL() A_SCROLL()
───────────────────────────────────────────────────────────────────────────────
A_KILL()
Release memory for all or a range of elements from a string array
───────────────────────────────────────────────────────────────────────────────
Syntax
A_KILL( <aTarget>, [<idxPos>], [<nCount>] )
Arguments
<aTarget> is the array in which to release elements.
<idxPos> is the position in the target array of the first element
to be released. If not specified, A_KILL() begins at the first
element in the array.
<nCount> is the number of elements to release. The default is all
elements starting at <idxPos> until the end of the array. This
includes wrapping.
Returns
None.
Description
FlexFile's string arrays are actually arrays of pointers. That is,
the array consists of four byte elements, each element capable of
pointing to a string of data. When declared, each element of the
array is undefined.
When data is A_STORE()d to an element of a string array, memory is
allocated for that data. If the array of pointers falls out of
scope before that memory is released, the memory is orphaned and
essentially lost until the application is exited.
Therefore, you must use A_KILL() to release the data before
allowing a FlexFile string array to fall out of scope.
┌────────────────────────── WARNING ───────────────────────────┐
│ You must use A_KILL() to release the data from a String type │
│ array before allowing the array to fall out of scope. │
└──────────────────────────────────────────────────────────────┘
Examples
// Use A_KILL() to release a FlexFile string type array
// before exiting the function that created it.
Phrase( "Miles to go before I sleep" )
FUNCTION Phrase( cSomePhrase )
LOCAL ffArray
// Declare a FlexFile string array.
ffArray = A_DECLARE( 'S', 5, 8 )
// Put the value into element A_( 3, 3 ).
A_STORE( ffArray, cSomePhrase, A_( 3, 3 ) )
// Do something with the array.
? A_RETRIEVE( ffArrary, A_(3, 3) )
// It is necessary to release the memory
// allocated for the string in the array.
A_KILL( ffArray )
// The array itself is released by Clipper on return
RETURN (.T.)
See Also: A_STORE() A_DECLARE()
───────────────────────────────────────────────────────────────────────────────
A_LEN()
Return the total number of elements in an array or a specified dimension
───────────────────────────────────────────────────────────────────────────────
Syntax
A_LEN( <aTarget>, [<nDimension>] ) -> nCount
Arguments
<aTarget> is the array whose elements will be counted.
<nDimension> is the dimension whose elements will be counted. If
not specified, all elements in the array will be counted.
Returns
A_LEN() returns the number of elements in the array or any one
dimension of the array.
Description
A_LEN() counts the number of declared elements in an array. If a
particular dimension is supplied in <nDimension>, then only the
number of elements in that dimension will be reported.
For example, if you declare a two dimensional array with thirty
students in the first dimension and their grades on five exams in
the second, A_LEN() will report thirty elements for the first
dimension, five elements in the second dimension or one hundred
and fifty elements in the entire array.
Examples
// Use A_LEN() to scan for the total length of an array.
LOCAL ffArray
// Declare a FlexFile string array.
ffArray = A_DECLARE( 'S', 5, 8 )
? A_LEN( ffArray ) // Result: 40
// Now use to show only the length of the second dimension.
? A_LEN( ffArray, 2 ) // Result: 8
See Also: A_DECLARE() A_ADD() A_SIZE()
───────────────────────────────────────────────────────────────────────────────
A_MAX()
Return the greatest value found in a numeric array
───────────────────────────────────────────────────────────────────────────────
Syntax
A_MAX( <aTarget>, [<idxPos>], [<nCount>] )
Arguments
<aTarget> is the numeric type array to evaluate.
<idxPos> is the position in the target array of the first element
to be evaluated. If not specified, A_MAX() begins its search on
the first element of the array.
<nCount> is the number of elements to evaluate. The default is all
elements starting at <idxPos> until the end of the row in the
current dimension.
Returns
A_MAX() returns an index to the element which has the maximum
value found in the specified range.
Description
A_MAX() evaluates every element in the specified range of a
numeric type array and returns an index to the largest element.
Examples
// Declare a two dimensional (I)nteger array
LOCAL rows, cols, x
rows = 10
cols = 20
aInteger = A_DECLARE( "I", rows, cols )
// Fill the second row of the array with increasing
// integer values 1 - 20
FOR x = 1 to 20
A_STORE( aInteger, x, A_( 2, x ) )
NEXT
// Fetch the maximum value in the first half of the
// second row.
? A_MAX( aInteger, A_( 2, 1 ), cols / 2 ) // Result: 10
See Also: A_MIN() A_SCAN()
───────────────────────────────────────────────────────────────────────────────
A_MIN()
Return the smallest value found in a given range of an array
───────────────────────────────────────────────────────────────────────────────
Syntax
A_MIN( <aTarget>, [<idxPos>], [<nCount>] )
Arguments
<aTarget> is the numeric type array to evaluate.
<idxPos> is the position in the target array of the first element
to be evaluated. If not specified, A_MIN() begins its search on
the first element in the array.
<nCount> is the number of elements to evaluate. The default is all
elements starting at <idxPos> until the end of the row in the
current dimension.
Returns
A_MIN() returns an index to the element which has the minimum
value found in the specified range.
Description
A_MIN() evaluates every element in the specified range of a
numeric type array and returns an index to that element which has
the lowest value.
Examples
// Declare a two dimensional (UI)nteger array
LOCAL rows, cols, x
rows = 10
cols = 20
auInteger = A_DECLARE( "UI", rows, cols )
// Fill the second row of the array with increasing
// integer values 1 - 20
FOR x = 1 to 20
A_STORE( auInteger, x, A_( 2, x ) )
NEXT
// Fetch the minimum value in the first half of the
// second row.
? A_MIN( auInteger, A_( 2, 1 ), cols / 2 ) // Result: 1
See Also: A_MAX() A_SCAN()
───────────────────────────────────────────────────────────────────────────────
A_MINUS()
Subtract all elements of an array by a value or an array of values
───────────────────────────────────────────────────────────────────────────────
Syntax
A_MINUS( <aTarget>,
<nValue> | <aValue>,
[<aSource>] )
Arguments
<aTarget> is the array to be modified. If <aSource> is not
specified then each element of <aTarget> will be subtracted by the
<nValue> or element by element by <aValue>.
<nValue> or <aValue> is either a value or an array of values to be
subtracted.
<aSource> is an optional array of values. If specified, then each
element of <aTarget> will be filled with the corresponding value
in <aSource> subtracted by <aValue> or <nValue>.
Returns
Although there is no return value, the <aTarget> array is modified.
Description
A_MINUS() provides a means to perform a subtraction of every
element in the <aSource> array if specified or the <aTarget> array
if not. Each element of the <aSource> or <aTarget> array has
<nValue> or <aValue> subtracted from it. The result of the
subtraction replaces the value in the <aTarget> array.
A_MINUS() can be performed on any integer or floating point type
array. These include the (T)iny, (I)nteger, (L)ong, (F)loat and
(D)ouble. Mixing of these types is allowed.
Notes
If the <aTarget> and the <aSource> arrays are not the same
dimensions, the results will be a subtraction element for element
as the arrays are aligned in memory. See chapter two for how
arrays are aligned in memory.
Examples
// Declare three double type arrays for this function.
LOCAL rows, cols
rows = 10
cols = 20
aSource = A_DECLARE( "D", rows, cols )
aTarget = A_DECLARE( "D", rows, cols )
aSubMe = A_DECLARE( "D", rows, cols )
// Fill the target array with 10's
A_FILL( aTarget, 10, A_(1, 1), rows * cols )
// Subtract 2 from every element in the target array
A_MINUS( aTarget, 2 )
? A_RETRIEVE( aTarget, A_( 4, 5 ) ) // Result: 8.0
// Fill the source array with 10's
A_FILL( aSource, 10, A_(1, 1), rows * cols )
// Subtract five from every element in the source array and
// put the result in the target array.
A_MINUS( aTarget, 5, aSource )
? A_RETRIEVE( aTarget, A_( 4, 5 ) ) // Result: 5.0
// Fill the "subtract me" array with 4's
// Remember the source array still has all 10's
A_FILL( aSubMe, 4, A_(1, 1), rows * cols )
// Subtract from every element in the source array the
// value in every matching element of the "subtract me"
// array and put the result in the target array.
A_MINUS( aTarget, aSubMe, aSource )
? A_RETRIEVE( aTarget, A_( 4, 5 ) ) // Result: 6.0
See Also: A_MULTIPLY() A_PLUS() A_DIVIDE()
───────────────────────────────────────────────────────────────────────────────
A_MULTIPLY()
Multiply all elements of an array by a value or an array of values
───────────────────────────────────────────────────────────────────────────────
Syntax
A_MULTIPLY( <aTarget>,
[<nValue> | <aValues>],
[<aSource>] )
Arguments
<aTarget> is the array to be modified. If <aSource> is not
specified then each element of <aTarget> will be multiplied by the
<nValue> or element by element by <aValue>.
<nValue> or <aValue> is either a value or an array of values to be
multiplied.
<aSource> is an optional array of values. If specified, then each
element of <aTarget> will be filled with the corresponding value
in <aSource> multiplied by <aValue> or <nValue>.
Returns
Although there is no return value, the <aTarget> array is modified.
Description
A_MULTIPLY() provides a means to perform a multiplication of every
element in the <aSource> array if specified or the <aTarget> array
if not. Each element of the <aSource> or <aTarget> array will be
multiplied by <nValue> or <aValue>. The result of the
multiplication replaces the value in the <aTarget> array.
A_MULTIPLY() can be performed on any integer or floating point
type array. These include the (T)iny, (I)integer, (L)ong, (F)loat
and (D)ouble (both signed and unsigned integers, of course).
Mixing of these types is allowed, however, if the <aTarget> array
is of integer type, decimals will be truncated.
Notes
If the <aTarget> and the <aSource> arrays are not the same
dimensions, the results will be a multiplication element for
element as the arrays are aligned in memory. See chapter two for
how arrays are aligned in memory.
Examples
// Declare three double type arrays for this function.
LOCAL rows, cols
rows = 10
cols = 20
aSource = A_DECLARE( "D", rows, cols )
aTarget = A_DECLARE( "D", rows, cols )
aMultiplier = A_DECLARE( "D", rows, cols )
// Fill the target array with 10's
A_FILL( aTarget, 10, A_(1, 1), rows * cols )
// Multiply every element in the target array by 2
A_MULTIPLY( aTarget, 2 )
? A_RETRIEVE( aTarget, A_( 4, 5 ) ) // Result: 20.0
// Fill the source array with 10's
A_FILL( aSource, 10, A_(1, 1), rows * cols )
// Multiply every element in the source array by five and
// put the result in the target array.
A_MULTIPLY( aTarget, 5, aSource )
? A_RETRIEVE( aTarget, A_( 4, 5 ) ) // Result: 50.0
// Fill the multiplier array with 4's
// Remember the source array still has all 10's
A_FILL( aMultiplier, 4, A_(1, 1), rows * cols )
// Multiply every element in the source array by every element
// in the multiplier array and put the result in the target
// array.
A_MULTIPLY( aTarget, aMultiplier, aSource )
? A_RETRIEVE( aTarget, A_( 4, 5 ) ) // Result: 40.0
See Also: A_DIVIDE() A_PLUS() A_MINUS()
───────────────────────────────────────────────────────────────────────────────
A_PLUS()
Add all elements of an array by a value or an array of values
───────────────────────────────────────────────────────────────────────────────
Syntax
A_PLUS( <aTarget>,
[<nValue> | <aValues>],
[<aSource>] )
Arguments
<aTarget> is the array to be modified. If <aSource> is not
specified then each element of <aTarget> will be added to <nValue>
or element by element by <aValue>.
<nValue> or <aValue> is either a value or an array of values to be
added.
<aSource> is an optional array of values. If specified, then each
element of <aTarget> will be filled with the corresponding value
in <aSource> added to <aValue> or <nValue>.
Returns
Although there is no return value, the <aTarget> array is
modified.
Description
A_PLUS() provides a means to perform an addition of every element
in the <aSource> array if specified or the <aTarget> array if not.
Each element of the <aSource> or <aTarget> array will be added to
<nValue> or <aValue>. The result of the addition is placed in the
<aTarget> array.
A_PLUS() can be performed on any integer or floating point type
array. These include the (T)iny, (I)integer, (L)ong, (F)loat and
(D)ouble (both signed and unsigned integers, of course). Mixing
of these types is allowed, however, if the <aTarget> array is of
integer type, decimals will be truncated.
Notes
If the <aTarget> and the <aSource> arrays are not the same
dimensions, the results will be an addition element for element as
the arrays are aligned in memory. See chapter two for how arrays
are aligned in memory.
Examples
// Declare three double type arrays for this function.
LOCAL rows, cols
rows = 10
cols = 20
aSource = A_DECLARE( "D", rows, cols )
aTarget = A_DECLARE( "D", rows, cols )
aAddMe = A_DECLARE( "D", rows, cols )
// Fill the target array with 10's
A_FILL( aTarget, 10, A_(1, 1), rows * cols )
// Add 2 to every element in the target array
A_ADD( @aTarget, 2 )
? A_RETRIEVE( aTarget, A_( 4, 5 ) ) // Result: 12.0
// Fill the source array with 10's
A_FILL( aSource, 10, A_(1, 1), rows * cols )
// Add five to every element in the source array and
// put the result in the target array.
A_ADD( @aTarget, 5, aSource )
? A_RETRIEVE( aTarget, A_( 4, 5 ) ) // Result: 15.0
// Fill the "add me" array with 4's
// Remember the source array still has all 10's
A_FILL( aAddMe, 4, A_(1, 1), rows * cols )
// Add to every element in the source array the
// value in every matching element of the "add me"
// array and put the result in the target array.
A_ADD( @aTarget, aAddMe, aSource )
? A_RETRIEVE( aTarget, A_( 4, 5 ) ) // Result: 14.0
See Also: A_DIVIDE() A_MULTIPLY() A_MINUS()
───────────────────────────────────────────────────────────────────────────────
A_RETRIEVE()
Return the value of a specified element of an array
───────────────────────────────────────────────────────────────────────────────
Syntax
A_RETRIEVE( <aTarget>,
<idxPos> ) -> expValue
Arguments
<aTarget> is the array to return a value from.
<idxPos> points to the element whose value is to be returned.
Returns
A_RETRIEVE() returns the value of the element in <aTarget> pointed
to by <idxPos>. The type of the value will always be the same
type as that of the array unless an error is encountered. In the
case of an error, the return value will be (.F.).
Description
A_RETRIEVE() is used to get the value of an element from a FlexFile
array.
Notes
■ If a numeric value was stored as a double or float, the value
returned will be equivalent to the value stored, but if you display the
value without a PICTURE clause, it will show the number of decimals
according to the value of SET DECIMAL TO (which is not necesarily the
same number of decimals that were visable when the value that was
A_STORE()ed).
Examples
// Declare a two dimensional (UL)ong integer array
LOCAL rows, cols
rows = 10
cols = 20
auLong = A_DECLARE( "UL", rows, cols )
// Fill the 5th row with 100,000
A_FILL( auLong, 123456, A_(5, 1), cols )
// Multiply the value in the fifth row, fourth column
// by five and print the result.
? A_RETRIEVE( auLong, A_( 5, 4 ) ) * 5 // Result: 617280
See Also: A_STORE() A_TYPE() A_DECLARE()
───────────────────────────────────────────────────────────────────────────────
A_SCAN()
Scan an array or part of an array for an expression
───────────────────────────────────────────────────────────────────────────────
Syntax
A_SCAN( <aTarget>,
<expSearch>,
[<idxPos>],
[<nCount>] ) -> nAbsPos
Arguments
<aTarget> The array to scan.
<expSearch> An expression that evaluates to the same type as the
<aTarget> array.
<idxPos> is the first position to in the <aTarget> array to be
scanned. If not specified, the default value is the first element
of the array.
<nCount> is the number of elements to scan starting at <idxPos>.
If not specified, elements are searched to the end of the array.
Returns
A_SCAN() returns an integer which is the absolute index pointer to
the first element found that matches . If no match is found
A_SCAN() returns 0. The <nAbsPos> can and should be used in any
further references to that element (see example below).
Description
A_SCAN() is used to search a FlexFile array for a value. A_SCAN()
will return an error if <expSearch> evaluates to a different type
than the <aTarget> array.
Wrapping will occur if <nCount> goes beyond the current row
pointed to by <idxPos>. For example, if you have a two
dimensional array with accounts represented by the first dimension
and 12 prior period amounts in the second dimension, and you
wanted to scan the first quarter (i.e. 3 periods) for a value,
making <nCount> three times the number of accounts will "wrap"
around three periods. See the discussion on wrapping in the
Arrays Chapter.
Examples
LOCAL ffArray, ptr, rows, cols
rows = 5
cols = 8
// Declare a FlexFile floating point type array.
ffArray = A_DECLARE( 'F', 5, 8 )
// Store a couple of values.
A_STORE( ffArray, 1234.56, A_( 3, 3 ) )
A_STORE( ffArray, 789.10, A_(4, 4) )
// Scan the entire array...
ptr = A_SCAN( ffArray, 789.10 )
? A_RETRIEVE( ffArray, ptr ) // Result: 789.10
// Scan starting at the first element in the fourth row.
? A_SCAN( ffArray, 1234.56, A( 4, 1 ) ) // Result: 0
// Scan starting at the first element in the third row.
ptr = A_SCAN( ffArray, 1234.56, A_( 3, 1 ) )
? A_RETRIEVE( ffArray, ptr ) // Result: 1234.56
// Scan starting at the first element in the second row
// but only count for one row.
? A_SCAN( ffArray, 1234.56, A_( 2, 1 ), cols ) // Result: 0
See Also: A_STORE() A_DECLARE() A_RETRIEVE()
───────────────────────────────────────────────────────────────────────────────
A_SCROLL()
Scroll all or part of an array
───────────────────────────────────────────────────────────────────────────────
Syntax
A_SCROLL( <aTarget>,
[<idxPos>],
[<nCount>],
[<nMagnitude>] )
Arguments
<aTarget> is the array in which elements will be scrolled.
<idxPos> is the first position to in the <aTarget> array to be
scrolled. If not specified, the default value is the first
element.
<nCount> is the number of elements to be scrolled starting at
<idxPos>. If not specified, elements are scrolled to the end of
the array.
<nMagnitude> indicates how far and in what direction to scroll the
array. A positive value scrolls the elements up (toward the
beginning); a negative value scrolls the elements down (towards
the end). If no value is specified, the defined range is scrolled
one element up.
Returns
None.
Description
A_SCROLL() scrolls all or a portion of a FlexFile array
<nMagnitude> number of elements. All elements at the end of the
scroll are lost and the new elements are initialized to null. Do
not confuse FlexFile's null with Clipper's NIL. A numeric null is
0, a logical null is (.F.) and a string null is "".
Wrapping will occur if <nCount> goes beyond the current row
pointed to by <idxPos>. For example, if you have a two
dimensional array with accounts represented by the first dimension
and 12 prior period amounts in the second dimension, and you
wanted to scroll the first quarter (i.e. 3 periods) up in the
array, making <nCount> three times the number of accounts will
"wrap" the scroll. Three periods worth of amounts will be
scrolled out of the array and three periods of 0's will be filled.
See the discussion on arrays in chapter 2 for more information on
this feature.
Examples
LOCAL ffArray, accounts, periods
accounts = 50
periods = 12
// Declare a FlexFile double precision type array.
ffArray = A_DECLARE( 'D', periods, accounts )
// Close out a quarter by scrolling three periods.
// (i.e. scroll all accounts by 3 periods.)
A_SCROLL( ffArray, , , -(accounts * 3) )
// Insert a new account just in front of the 7th account.
accounts++
A_SIZE( @ffArray, periods, accounts )
FOR x = 1 to periods
A_SCROLL( ffArray, A_( x, 7 ), accounts - 7, -1 )
NEXT
See Also: A_INS() A_DEL() A_SCAN()
───────────────────────────────────────────────────────────────────────────────
A_SIZE()
Dynamically change the size or dimensions of an array
───────────────────────────────────────────────────────────────────────────────
Syntax
A_SIZE( @<aTarget>,
<nElements>,
[, <nElements>...] ) -> lSuccess
Arguments
<aTarget> is the array to re-size. It must be passed-by-
reference. A_SIZE() cannot resize binary type arrays.
<nElements> are the new dimensions of the array.
Returns
A_SIZE() returns (.T.) if the function was successful, otherwise,
it returns (.F.). In addition, the aTarget array is modified.
Description
A_SIZE() modifies the dimensions of the @<aTarget>. If the
array is increased in size, the new elements will be added to the
end of the array and assigned to null. If the array is decreased
in size, the data at the end of the array will be lost.
You cannot change the number of dimensions with A_SIZE(), rather,
you can change only their magnitude.
Examples
// Declare a two dimensional (L)ong integer array
LOCAL rows, cols, x, y, z := 0
rows = 10
cols = 20
aLong = A_DECLARE( "L", rows, cols )
// Fill the array with increasing values.
FOR x = 1 to rows
FOR y = 1 to cols
A_STORE( aLong, z++, A_( x, y ) )
NEXT
NEXT
// Increase the size of the array by 10 rows
A_SIZE( @aLong, rows + 10, cols )
// Show some results.
? A_RETRIEVE( aLong, 10, 20 ) // Result: 200
? A_RETRIEVE( aLong, 11, 1 ) // Result: 0
See Also: A_ADD() A_DEL() A_INS()
───────────────────────────────────────────────────────────────────────────────
A_SORT()
Sort all or part of an array in ascending or descending order
───────────────────────────────────────────────────────────────────────────────
Syntax
A_SORT( <aTarget>,
[<idxPos>],
[<nCount>],
[<cOrder>] )
Arguments
<aTarget> is the array to be sorted. A_SORT will not sort a Binary
"logical" type array.
<idxPos> is the position in the <aTarget> array to begin the sort.
If not specified, the default value is one.
<nCount> is the number of elements beginning at <idxPos> to sort.
If not specified, all elements are sorted to the end of the row in
the current dimension.
<cOrder> is either "Ascending" or "Descending". Only the first
letter is significant. If not specified, the sort is made
"Ascending".
Returns
None.
Description
A_SORT() will sort all or part of a FlexFile array. If <cOrder>
is ascending, elements with higher values are stored at the top
(beginning) of the array. If <cOrder> is descending, elements
with lower values are stored at the top of the array.
Wrapping will occur if <nCount> goes beyond the current row
pointed to by <idxPos>.
Examples
// Declare a two dimensional (C)haracter array
LOCAL rows, cols, x, y, z
rows = 10
cols = 20
z = 1
aChar = A_DECLARE( "C", rows, cols )
// Fill the array with increasing character values.
FOR x = 1 to rows
FOR y = 1 to cols
z++
A_STORE( aChar, chr(z), A_( x, y ) )
NEXT
NEXT
// Sort the first row in descending order
A_SORT( aChar, A_( 1, 1 ), cols, 'D' )
// Show the results
? A_RETRIEVE( aChar, A_( 1, 1 ) ) // Result:
? A_RETRIEVE( aChar, A_( 1, 20) ) // Result:
See Also: A_SCAN()
───────────────────────────────────────────────────────────────────────────────
A_STORE()
Store an expression into an element of an array
───────────────────────────────────────────────────────────────────────────────
Syntax
A_STORE( <aTarget>,
<expValue>,
<idxPos> ) -> lSuccess
Arguments
<aTarget> is the array in which to store <expValue>.
<expValue> is any simple Clipper expression that evaluates to the
same type as the array.
<idxPos> is the index to the element whose value will become
<expValue>.
Returns
A_STORE() returns (.T.) if successful, otherwise, (.F.).
Description
A_STORE() stores a value in a FlexFile type array. The value
being stored must be of the same type as that of the array.
Storing a value with a different type than the array is considered
an error and A_STORE() will return (.f.).
┌────────────────────────── WARNING ───────────────────────────┐
│ You must use A_KILL() to release the data that has been type │
│ A_STORE()d to a string type array before allowing the array │
│ to fall out of scope. │
└──────────────────────────────────────────────────────────────┘
Examples
// Declare a two dimensional (C)haracter array
LOCAL rows, cols, x, y, z
rows = 10
cols = 20
z = 1
aChar = A_DECLARE( "C", rows, cols )
// Fill the array with increasing character values.
FOR x = 1 to rows
FOR y = 1 to cols
z++
A_STORE( aChar, chr(z), A_( x, y ) )
NEXT
NEXT
// Print out the results: All ASCII characters from one
// to 200. If you run this program it will beep an sqauk
// and generally look ugly.
FOR x = 1 to rows
FOR y = 1 to cols
z++
? A_RETRIEVE( aChar, A_( x, y ) )
NEXT
NEXT
See Also: A_KILL() A_FILL() A_RETRIEVE()
───────────────────────────────────────────────────────────────────────────────
A_TYPE()
Return the type of an array
───────────────────────────────────────────────────────────────────────────────
Syntax
A_TYPE( <aTarget> ) -> cType
Arguments
<aTarget> is the array whose type is returned.
Returns
A_TYPE() returns the type of the <aTarget> as a character
string.
Description
A_TYPE() returns the type of a FlexFile array. If the array is of
integer type and was declared as unsigned, then a "U" will precede
the type.
The return value is a character string such as "L" for a long
integer or "UL" for an unsigned long integer.
Appendix C lists all the array types and sizes.
Examples
// Setup seven variables of differing array types.
LOCAL aLogical ,;
aChar ,;
aTiny ,;
auTiny ,;
aInteger ,;
auInteger ,;
aLong ,;
auLong ,;
aString ,;
aFloat ,;
aDouble
// Declare an array for each possible type.
aLogical = A_DECLARE( 'B', 10 )
aChar = A_DECLARE( 'C', 10 )
aTiny = A_DECLARE( 'T', 10 )
auTiny = A_DECLARE( 'UT', 10 )
aInteger = A_DECLARE( 'I', 10 )
auInteger = A_DECLARE( 'UI', 10 )
aLong = A_DECLARE( 'L', 10 )
auLong = A_DECLARE( 'UL', 10 )
aString = A_DECLARE( 'S', 10 )
aFloat = A_DECLARE( 'F', 10 )
aDouble = A_DECLARE( 'D', 10 )
// Show the type of each array
? A_TYPE( aLogical ) // Result: B
? A_TYPE( aChar ) // Result: C
? A_TYPE( aTiny ) // Result: T
? A_TYPE( auTiny ) // Result: UT
? A_TYPE( aInteger ) // Result: I
? A_TYPE( auInteger) // Result: UI
? A_TYPE( aLong ) // Result: L
? A_TYPE( auLong ) // Result: UL
? A_TYPE( aString ) // Result: S
? A_TYPE( aFloat ) // Result: F
? A_TYPE( aDouble ) // Result: D
See Also: A_DECLARE()
Error Codes Introduction
Changes:
The FlexFile error system has been changed significatly in
releases after 1.01. The main differences are that:
1. Any call to any FlexFile V_xxx() function will reset
the V_ERROR() to zero. Any call to any A_xxx() function
will reset the A_ERROR() to zero. This prevents an error
setting from appearing in a context other than what
caused it.
2. Two error codes are now maintained. One for the V_xxx()
functions and the other for the A_xxx() functions.
3. Calling either V_ERROR() or A_ERROR() no longer resets
the error code. Therefore, you can now make multiple calls
to the desired error reporting routine without loosing the
error.
General Information
The FlexFile error system is accessed by either of the two
functions A_ERROR() or V_ERROR(). These functions can be
accessed from the debugger's expression window or placed
directly in your code. Of course, if you plan to use either
function from the debugger you must refer to them with either
the EXTERNAL command or explicitly within your code in order
for the LINKER to include them in your application.
The error functions will report the last error that
was trapped by FlexFile as a numeric integer. For example, if
you attempt to V_USE() a file which is already being used
exclusivly by another user on a network, V_USE() will return a
-1 and V_ERROR() will return 5052. However, if you do not
test the V_ERROR() return value until after you try to
V_REPLACE() a value (remember no file was opened), then
V_ERROR() will return 7301 indicatating a V_REPLACE() error.
Ignore any numbers in the 10's and 1'n place in the return
value from the error functions; these are used by the
FlexFile support staff. So, for example, 7301 and 7302 both
refer to the 7300 class of error.
These functions, when called, reset the error value to zero
which indicates that no error has occurred.
See Also: V_ERROR() A_ERROR()
──────────────────────────────────────────────────────────────────────────────
Error 0100 Not an array.lib type array.
──────────────────────────────────────────────────────────────────────────────
A parameter passed is not a FlexFile strongly typed array but
should have been.
The most usual cause of this problem is that a parameter is
out of order in the parameter list of a FlexFile function or a
Clipper array was passed instead of a FlexFile type array.
See Also: A_DECLARE()
──────────────────────────────────────────────────────────────────────────────
Error 0300 Invalid A_DECLARE() dimensions
──────────────────────────────────────────────────────────────────────────────
An attempt was made to declare an array with greater than six
dimensions or zero dimensions.
See Also: A_DECLARE()
──────────────────────────────────────────────────────────────────────────────
Error 0400 Attempt to address an out of bounds element.
──────────────────────────────────────────────────────────────────────────────
An attempt was made to address an element which is outside of
the bounds of the FlexFile array.
For example, if you declare a single dimensional array with
twenty elements and then try to access the twenty first
element (or an element in the second or greater dimension),
A_ERROR() will return 400.
See Also: A_RETRIEVE() A_DECLARE()
──────────────────────────────────────────────────────────────────────────────
Error 0500 Attempt to declare an array larger than 65535 bytes.
──────────────────────────────────────────────────────────────────────────────
Because the elements of different FlexFile array types require
different amounts of space, you must look at each array type
to determine the maximum number of elements possible.
The limiting factore in FlexFile arrays of every type is 65535
bytes. So, for example, an integer array which requires only
two bytes per element will reach this limit when you attempt
to declare an integer array of slightly more than 32,000
elements.
See the reference to A_DECLARE() for a table of array type and
required sizes per element.
See Also: A_DECLARE()
──────────────────────────────────────────────────────────────────────────────
Error 0600 Attempt to declare an array with an invalid type.
──────────────────────────────────────────────────────────────────────────────
The arrays type must be one that FlexFile recognizes. These
are listed in the A_DECLARE() function reference.
See Also: A_DECLARE()
──────────────────────────────────────────────────────────────────────────────
Error 0700 Attempt to declare an array with invalid dimensions.
──────────────────────────────────────────────────────────────────────────────
A FlexFile type array may have a minimum of one and a maximum of
six dimensions.
See Also: A_DECLARE()
──────────────────────────────────────────────────────────────────────────────
Error 0800 Out of memory on an attempt to declare an array
──────────────────────────────────────────────────────────────────────────────
In S'87 and the initial release of Clipper 5.0 it is a fatal
error when an attempt is made to declare an array larger than
available memory. However, this is a bug in 5.0 and when
fixed this error will indicate that condition and the error
system will be automatically called.
See Also: A_DECLARE()
──────────────────────────────────────────────────────────────────────────────
Error 2000 Array type incompatible with function called.
──────────────────────────────────────────────────────────────────────────────
The array function called cannot operate on the type of the
array passed. For example, if you pass A_SORT() a binary type
array, no sort will be performed and this error will be set.
See Also: A_SORT() A_SCAN()
──────────────────────────────────────────────────────────────────────────────
Error 2100 Function called is not valid on binary arrays.
──────────────────────────────────────────────────────────────────────────────
Binary (logical) arrays are not supported by many of the FlexFile
array functions. For example, you cannot A_SCAN() an array for a
logical (.t.) or (.f.) and you cannot A_SORT(). The FlexFile
array functions which operate on binary arrays are:
A_STORE()
A_RETRIEVE()
A_LEN()
A_TYPE()
──────────────────────────────────────────────────────────────────────────────
Error 2200 Divide by zero in a FlexFile array math function.
──────────────────────────────────────────────────────────────────────────────
FlexFile's math functions such as A_DIVIDE() are protected against
a programmer error of division by zero. In the event this error
occurs, the division results in a zero value and this error is
flagged.
See Also: A_DIVIDE()
──────────────────────────────────────────────────────────────────────────────
Error 2300 A_COPY() used with incompatable array types
──────────────────────────────────────────────────────────────────────────────
An attempt was made to use A_COPY() on two different array types.
A_COPY() must be performed on arrays of the same type.
See Also: A_COPY()
──────────────────────────────────────────────────────────────────────────────
Error 3000 Bad or missing parameter in array function.
──────────────────────────────────────────────────────────────────────────────
A parameter was passed to a FlexFile array function that is not
the correct type or a parameter is required that was not passed.
The most usual cause of this problem is that a parameter is
out of order in the parameter list of a FlexFile function or a
parameter was inadvertently omitted.
──────────────────────────────────────────────────────────────────────────────
Error 3100 A_SIZE() error in dimensions.
──────────────────────────────────────────────────────────────────────────────
A_SIZE() cannot change the number of an arrays dimensions, rather,
it can only change the magnitude of each dimension. An attempt
was made to change the number of dimensions.
See Also: A_SIZE() A_ADD()
──────────────────────────────────────────────────────────────────────────────
Error 5000 Unable to open file.
──────────────────────────────────────────────────────────────────────────────
This error can result from many different situations:
Disk access denied (disk full).
Create error.
Impropper file name (directory does not exist).
File opened exclusively by other user.
See Also: V_USE() V_VLF2FILE() network.ngo:Network Programming
──────────────────────────────────────────────────────────────────────────────
Error 5200 Attempt to lock header failed.
──────────────────────────────────────────────────────────────────────────────
This error occurs when V_USE() fails to get a lock on the header
of the DBV file.
See Also: V_USE() "Error 5000"
──────────────────────────────────────────────────────────────────────────────
Error 5300 Out of memory.
──────────────────────────────────────────────────────────────────────────────
This error occurs when FlexFile fails to allocate memory using
Clipper's extend system. Because FlexFile requires very little
memory, this is an unusual error and indicates extremely low memory
conditions.
──────────────────────────────────────────────────────────────────────────────
Error 6000 Invalid or missing parameter.
──────────────────────────────────────────────────────────────────────────────
This error occurs whenever FlexFile detects a parameter of the
wrong type or a missing parameter when one is required.
The most usual cause of this problem is that a parameter is
out of order in the parameter list of a FlexFile function.
──────────────────────────────────────────────────────────────────────────────
Error 6200 Bad pointer-field passed.
──────────────────────────────────────────────────────────────────────────────
FlexFile generates a six byte character string which points to
data stored in a DBV file. If you attempt to use anything but
a FlexFile generated pointer-field in an attempt to access data
in a DBV file, this error will be generated. The single exception
to this rule is an empty pointer-field which must be six (or more)
spaces.
──────────────────────────────────────────────────────────────────────────────
Error 6300 Invalid data type in v_replace().
──────────────────────────────────────────────────────────────────────────────
The valid data types for V_REPLACE() are defined in the V_REPLACE()
function reference. If FlexFile encounters a type which is not
amoung these valid types, this error will be generated.
In addition, if you make the mistake of using a pointer-field which
points into one file as a pointer-field for a different file, FlexFile
will again generate this error. This is because the pointer-field
is pointing to something other than the header which FlexFile pre-
pends to each field.
──────────────────────────────────────────────────────────────────────────────
Error 6400 Proclip(tm) window is not a compatible version.
──────────────────────────────────────────────────────────────────────────────
An attempt was made to restore a Proclip window which has been
stored to a DBV file while using an incompatible (most probably newer)
version of Proclip.
You will need a newer version of FlexFile to accomodate the new
Proclip window type.
──────────────────────────────────────────────────────────────────────────────
Error 6500 VLF length exceeds Clipper's maximum 65535 bytes.
──────────────────────────────────────────────────────────────────────────────
In certain circumstances it is possible to create a VLF that is
larger than 64K. The V_STUFF() function is capable of increasing
the size of a field with no regard for memory, and the V_FILE2VLF()
function will easily create fields that are extremely large.
If you then attempt to V_RETRIEVE() this data, this error will be
set and V_RETRIEVE() will fail.
See Also: V_RETRIEVE() V_STUFF() dbvfunc.ngo V_REPLACE()
──────────────────────────────────────────────────────────────────────────────
Error 6700 Error in V_RETRIEVE(). Array corrupted.
──────────────────────────────────────────────────────────────────────────────
An attempt was made to retrieve an array from a DBV but the array
was found to be corrupted. V_PACK()ing the file is recommended.
See Also: V_RETRIEVE()
──────────────────────────────────────────────────────────────────────────────
Error 7100 Write error. The disk may be full.
──────────────────────────────────────────────────────────────────────────────
An attemt to write data failed. If the file was in use in shared
mode, this error can still occur after the lock has been secured.
The most likely cause for this error is no remaining disk space.
──────────────────────────────────────────────────────────────────────────────
Error 7200 Record locking time out.
──────────────────────────────────────────────────────────────────────────────
This is an error code for future versions of FlexFile.
──────────────────────────────────────────────────────────────────────────────
Error 7300 File not open in currently V_SELECTed() area.
──────────────────────────────────────────────────────────────────────────────
Attempt was made to access a DBV file in a FlexFile work area
that does not have a file open. For example,
V_CLOSE()
V_REPLACE( "Try and put this away", ptr_fld )
See Also: V_SELECT() V_ALIAS()
──────────────────────────────────────────────────────────────────────────────
Error 7400 Selected area is out of bounds. See v_files().
──────────────────────────────────────────────────────────────────────────────
You can only V_SELECT() an area within the current V_FILES() setting.
The default setting for the maximum number of areas is 10.
See Also: V_FILES() V_SELECT()
──────────────────────────────────────────────────────────────────────────────
Error 7500 Unknown alias.
──────────────────────────────────────────────────────────────────────────────
An attempt was made to V_SELECT() (or pass a function that accepts
an area as a parameter) an illegal or unknown alias.
See Also: V_ALIAS() V_SELECT() V_USE()
─────────────────────────────────────────────────────────────────────────────
Error 7600 Illegal attempt to reset V_FILES() or V_BUFFERS
─────────────────────────────────────────────────────────────────────────────
An attempt was made to set V_FILES() or V_BUFFERS() when there was at
least one DBV file open. You must first close all DBV files before
attempting to use these functions.
See Also: V_FILES() V_BUFFERS()
─────────────────────────────────────────────────────────────────────────────
Error 7700 Invalid V_RETRIEVE() substring parameters.
─────────────────────────────────────────────────────────────────────────────
When using V_RETRIEVE() to retrieve only part of a VLF, the parameters
representing the bounds of the string to retrieve were invalid.
See Also: V_RETRIEVE()
──────────────────────────────────────────────────────────────────────────────
Error 8000 No areas remain. See v_files() to increase the quantity of areas.
──────────────────────────────────────────────────────────────────────────────
An attempt was made to V_USE() with the "NEW" modifier or V_SELECT(0)
when all available areas are in use. The solution to this problem
is to increase your maximum FlexFile work areas with the V_FILES()
function.
See Also: V_FILES() V_SELECT() V_USE()
──────────────────────────────────────────────────────────────────────────────
Error 8100 Not a VLF type file.
──────────────────────────────────────────────────────────────────────────────
Attempt was made to V_USE() a file that is not recognizable as a
FlexFile type file.
See Also: V_USE()
──────────────────────────────────────────────────────────────────────────────
Error 8200 File read error.
──────────────────────────────────────────────────────────────────────────────
This error is possible if an exclusive lock has been imposed
by another work station on a network.
See Also: V_EXCLUSIV()
──────────────────────────────────────────────────────────────────────────────
Error 8300 Unknown data type. Possibly using a pointer-field from wrong work area.
──────────────────────────────────────────────────────────────────────────────
The valid data types for V_REPLACE() and V_RETRIEVE() are defined
in the V_REPLACE() function reference. If FlexFile encounters a
type which is not amoung these valid types, this error will be
generated. For example, using V_RETRIEVE() to try to fetch a Summer
87 array will create this error. Use V_FILLARR() instead.
In addition, if you make the mistake of using a pointer-field which
points into one file as a pointer-field for a different file, FlexFile
will again generate this error. This is because the pointer-field
is pointing to something other than the header which FlexFile pre-
pends to each field.
──────────────────────────────────────────────────────────────────────────────
Error 8400 Attempt to V_RETRIEVE a Proclip window.
──────────────────────────────────────────────────────────────────────────────
An attempt was made to V_RETRIEVE() a Proclip window. Use
V_WNDCREAT() to create a Proclip window that has been stored
to a DBV file.
See Also: V_WNDCREAT()
──────────────────────────────────────────────────────────────────────────────
Error 9000 Undetermined error in seek for deleted space.
──────────────────────────────────────────────────────────────────────────────
Unfortunately, this spells disaster and is usually caused by a
corrupted DBV file. However, the FlexFile scheme is based on
a very solid principal. That is, if your DBF file holding the
pointer-fields into the DBV file is intact, the V_RETRIEVE()
function will almost invariably still retrieve data from any
field that has not yet been corrupted.
So, disaster recovery is as simple as creating a new DBV file
in a new FlexFile work area and V_REPLACE()ing data into the
new area with the data V_RETRIEVE()d from the corrupted area.
Remember to test for V_TYPE() on the pointer-field because if
it encounters a corrupted portion of the file, it will return
an "U"ndefined regardless of the data type stored in that
particular field.